diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index 1a1409c654d..0b055ca8042 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -612,6 +612,14 @@ pub(super) struct Channel { /// `accept_inbound_channel`, and `funding_created` should therefore not execute successfully. inbound_awaiting_accept: bool, + /// A flag that indicates whether the channel requires the user to signal readiness to send + /// the `msgs::ChannelReady` message. This is only set to true if the channel was created with a + /// `ChannelHandshakeConfig::manually_signal_channel_ready` flag set to true. + /// + /// When a user signals readiness via `ChannelManager::signal_channel_readiness` this flag is + /// flipped to false. + requires_manual_readiness_signal: bool, + /// The hash of the block in which the funding transaction was included. funding_tx_confirmed_in: Option, funding_tx_confirmation_height: u32, @@ -1049,6 +1057,7 @@ impl Channel { target_closing_feerate_sats_per_kw: None, inbound_awaiting_accept: false, + requires_manual_readiness_signal: config.channel_handshake_config.manually_signal_channel_ready, funding_tx_confirmed_in: None, funding_tx_confirmation_height: 0, @@ -1393,6 +1402,7 @@ impl Channel { target_closing_feerate_sats_per_kw: None, inbound_awaiting_accept: true, + requires_manual_readiness_signal: config.channel_handshake_config.manually_signal_channel_ready, funding_tx_confirmed_in: None, funding_tx_confirmation_height: 0, @@ -4968,10 +4978,11 @@ impl Channel { self.channel_update_status = status; } - fn check_get_channel_ready(&mut self, height: u32) -> Option { + pub fn check_get_channel_ready(&mut self, height: u32) -> Option { // Called: // * always when a new block/transactions are confirmed with the new height // * when funding is signed with a height of 0 + // * when user calls ChannelManager::signal_channel_readiness if self.funding_tx_confirmation_height == 0 && self.minimum_depth != Some(0) { return None; } @@ -5273,6 +5284,10 @@ impl Channel { self.inbound_awaiting_accept } + pub fn requires_manual_readiness_signal(&self) -> bool { + self.requires_manual_readiness_signal + } + /// Sets this channel to accepting 0conf, must be done before `get_accept_channel` pub fn set_0conf(&mut self) { assert!(self.inbound_awaiting_accept); @@ -6426,6 +6441,8 @@ impl Writeable for Channel { // versions prior to 0.0.113, the u128 is serialized as two separate u64 values. Therefore, // we write the high bytes as an option here. let user_id_high_opt = Some((self.user_id >> 64) as u64); + + let requires_manual_readiness_signal = Some(self.requires_manual_readiness_signal); write_tlv_fields!(writer, { (0, self.announcement_sigs, option), @@ -6452,6 +6469,7 @@ impl Writeable for Channel { (23, channel_ready_event_emitted, option), (25, user_id_high_opt, option), (27, self.channel_keys_id, required), + (29, requires_manual_readiness_signal, option) }); Ok(()) @@ -6723,6 +6741,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch let mut user_id_high_opt: Option = None; let mut channel_keys_id: Option<[u8; 32]> = None; + let mut requires_manual_readiness_signal: Option = Some(false); read_tlv_fields!(reader, { (0, announcement_sigs, option), @@ -6743,6 +6762,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch (23, channel_ready_event_emitted, option), (25, user_id_high_opt, option), (27, channel_keys_id, option), + (29, requires_manual_readiness_signal, option), }); let (channel_keys_id, holder_signer) = if let Some(channel_keys_id) = channel_keys_id { @@ -6853,6 +6873,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch target_closing_feerate_sats_per_kw, inbound_awaiting_accept: false, + requires_manual_readiness_signal: requires_manual_readiness_signal.unwrap(), funding_tx_confirmed_in, funding_tx_confirmation_height, diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index c4ebc8bc3fa..86f412ad0d2 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -1449,6 +1449,22 @@ macro_rules! remove_channel { } } +macro_rules! channel_ready_pending { + ($self: ident, $pending_msg_events: expr, $channel: expr, $channel_ready_msg: expr) => {{ + if $channel.requires_manual_readiness_signal() { + let mut pending_events = $self.pending_events.lock().unwrap(); + pending_events.push(events::Event::PendingChannelReady { + channel_id: $channel.channel_id(), + user_channel_id: $channel.get_user_id(), + counterparty_node_id: $channel.get_counterparty_node_id(), + funding_outpoint: $channel.get_funding_txo().unwrap(), + }); + } else { + send_channel_ready!($self, $pending_msg_events, $channel, $channel_ready_msg); + } + }} +} + macro_rules! send_channel_ready { ($self: ident, $pending_msg_events: expr, $channel: expr, $channel_ready_msg: expr) => {{ $pending_msg_events.push(events::MessageSendEvent::SendChannelReady { @@ -4175,7 +4191,7 @@ where } if let Some(msg) = channel_ready { - send_channel_ready!(self, pending_msg_events, channel, msg); + channel_ready_pending!(self, pending_msg_events, channel, msg); } if let Some(msg) = announcement_sigs { pending_msg_events.push(events::MessageSendEvent::SendAnnouncementSignatures { @@ -4354,6 +4370,38 @@ where Ok(()) } + /// Signals channel readiness after a [`Event::PendingChannelReady`]. + /// + /// The `channel_id` parameter indicates which channel should signal readiness, + /// and the `counterparty_node_id` parameter is the id of the peer the channel is with. + /// + /// [`Event::PendingChannelReady`]: events::Event::PendingChannelReady + pub fn signal_channel_readiness(&self, channel_id: &[u8; 32], counterparty_node_id: &PublicKey) -> Result<(), APIError> { + let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier); + + let per_peer_state = self.per_peer_state.read().unwrap(); + let peer_state_mutex = per_peer_state.get(counterparty_node_id) + .ok_or_else(|| APIError::ChannelUnavailable { err: format!("Can't find a peer matching the passed counterparty node_id {}", counterparty_node_id) })?; + let mut peer_state_lock = peer_state_mutex.lock().unwrap(); + let peer_state = &mut *peer_state_lock; + let pending_msg_events = &mut peer_state.pending_msg_events; + match peer_state.channel_by_id.entry(channel_id.clone()) { + hash_map::Entry::Occupied(mut channel) => { + let best_block_height = self.best_block.read().unwrap().height(); + let channel = channel.get_mut(); + match channel.check_get_channel_ready(best_block_height) { + Some(channel_ready) => send_channel_ready!(self, pending_msg_events, channel, channel_ready), + None => return Err(APIError::APIMisuseError { err: "The channel isn't currently in a state where we can signal readiness.".to_owned() }) + } + } + hash_map::Entry::Vacant(_) => { + return Err(APIError::ChannelUnavailable { err: format!("Channel with id {} not found for the passed counterparty node_id {}", log_bytes!(*channel_id), counterparty_node_id) }); + } + } + + Ok(()) + } + /// Gets the number of peers which match the given filter and do not have any funded, outbound, /// or 0-conf channels. /// @@ -5953,7 +6001,7 @@ where HTLCDestination::NextHopChannel { node_id: Some(channel.get_counterparty_node_id()), channel_id: channel.channel_id() })); } if let Some(channel_ready) = channel_ready_opt { - send_channel_ready!(self, pending_msg_events, channel, channel_ready); + channel_ready_pending!(self, pending_msg_events, channel, channel_ready); if channel.is_usable() { log_trace!(self.logger, "Sending channel_ready with private initial channel_update for our counterparty on channel {}", log_bytes!(channel.channel_id())); if let Ok(msg) = self.get_channel_update_for_unicast(channel) { diff --git a/lightning/src/util/config.rs b/lightning/src/util/config.rs index b32e8660d0a..5c6c9dfda4d 100644 --- a/lightning/src/util/config.rs +++ b/lightning/src/util/config.rs @@ -149,6 +149,17 @@ pub struct ChannelHandshakeConfig { /// Maximum value: 1,000,000, any values larger than 1 Million will be treated as 1 Million (or 100%) /// instead, although channel negotiations will fail in that case. pub their_channel_reserve_proportional_millionths: u32, + /// If this is set to true, the user needs to manually signal readiness for an inbound channel. + /// + /// When set to true, [`Event::PendingChannelReady`] will be triggered once LDK has seen sufficient + /// confirmations of the funding transaction. In that case, a [`msgs::ChannelReady`] message will not be + /// sent to the counterparty node unless the user explicitly chooses to signal readiness. + /// + /// Default value: false. + /// + /// [`Event::PendingChannelReady`]: crate::util::events::Event::PendingChannelReady + /// [`msgs::ChannelReady`]: crate::ln::msgs::ChannelReady + pub manually_signal_channel_ready: bool, #[cfg(anchors)] /// If set, we attempt to negotiate the `anchors_zero_fee_htlc_tx`option for outbound channels. /// @@ -183,6 +194,7 @@ impl Default for ChannelHandshakeConfig { announced_channel: false, commit_upfront_shutdown_pubkey: true, their_channel_reserve_proportional_millionths: 10_000, + manually_signal_channel_ready: false, #[cfg(anchors)] negotiate_anchors_zero_fee_htlc_tx: false, } diff --git a/lightning/src/util/events.rs b/lightning/src/util/events.rs index 8c981fd1c48..96e75a42681 100644 --- a/lightning/src/util/events.rs +++ b/lightning/src/util/events.rs @@ -15,6 +15,7 @@ //! few other things. use crate::chain::keysinterface::SpendableOutputDescriptor; +use crate::chain::transaction::OutPoint; #[cfg(anchors)] use crate::ln::chan_utils::{self, ChannelTransactionParameters, HTLCOutputInCommitment}; use crate::ln::channelmanager::{InterceptId, PaymentId}; @@ -817,6 +818,50 @@ pub enum Event { /// transaction. claim_from_onchain_tx: bool, }, + /// Indicates a channel has received sufficient confirmations and LDK is ready to send [`msgs::ChannelReady`]. + /// + /// To signal readiness, call [`ChannelManager::signal_channel_readiness`]. To reject the + /// request, call [`ChannelManager::force_close_without_broadcasting_txn`]. + /// + /// The event is only triggered when the [`UserConfig::manually_signal_channel_ready`] + /// config flag is set to true. + /// + /// [`ChannelManager::signal_channel_readiness`]: crate::ln::channelmanager::ChannelManager::signal_channel_readiness + /// [`ChannelManager::force_close_without_broadcasting_txn`]: crate::ln::channelmanager::ChannelManager::force_close_without_broadcasting_txn + /// [`UserConfig::manually_signal_channel_ready`]: crate::util::config::UserConfig::manually_signal_channel_ready + /// [`msgs::ChannelReady`]: crate::ln::msgs::ChannelReady + PendingChannelReady { + /// The channel ID of the channel. + /// + /// When responding to the request, the `channel_id` should be passed + /// back to the ChannelManager through [`ChannelManager::signal_channel_readiness`] to signal, + /// or through [`ChannelManager::force_close_without_broadcasting_txn`] to reject. + /// + /// [`ChannelManager::signal_channel_readiness`]: crate::ln::channelmanager::ChannelManager::signal_channel_readiness + /// [`ChannelManager::force_close_without_broadcasting_txn`]: crate::ln::channelmanager::ChannelManager::force_close_without_broadcasting_txn + channel_id: [u8; 32], + /// The `user_channel_id` value passed in to [`ChannelManager::create_channel`] for outbound + /// channels, or to [`ChannelManager::accept_inbound_channel`] for inbound channels if + /// [`UserConfig::manually_accept_inbound_channels`] config flag is set to true. Otherwise + /// `user_channel_id` will be randomized for an inbound channel. + /// + /// [`ChannelManager::create_channel`]: crate::ln::channelmanager::ChannelManager::create_channel + /// [`ChannelManager::accept_inbound_channel`]: crate::ln::channelmanager::ChannelManager::accept_inbound_channel + /// [`UserConfig::manually_accept_inbound_channels`]: crate::util::config::UserConfig::manually_accept_inbound_channels + user_channel_id: u128, + /// The node_id of the counterparty requesting to open the channel. + /// + /// When responding to the request, the `counterparty_node_id` should be passed + /// back to the `ChannelManager` through [`ChannelManager::signal_channel_readiness`] to + /// signal readiness, or through [`ChannelManager::force_close_without_broadcasting_txn`] to reject the + /// request. + /// + /// [`ChannelManager::signal_channel_readiness`]: crate::ln::channelmanager::ChannelManager::signal_channel_readiness + /// [`ChannelManager::force_close_without_broadcasting_txn`]: crate::ln::channelmanager::ChannelManager::force_close_without_broadcasting_txn + counterparty_node_id: PublicKey, + /// The outpoint that holds the channel funds on-chain. + funding_outpoint: OutPoint, + }, /// Used to indicate that a channel with the given `channel_id` is ready to /// be used. This event is emitted either when the funding transaction has been confirmed /// on-chain, or, in case of a 0conf channel, when both parties have confirmed the channel @@ -1136,6 +1181,15 @@ impl Writeable for Event { (6, channel_type, required), }); }, + &Event::PendingChannelReady { ref channel_id, ref user_channel_id, ref counterparty_node_id, ref funding_outpoint } => { + 31u8.write(writer)?; + write_tlv_fields!(writer, { + (0, channel_id, required), + (2, user_channel_id, required), + (4, counterparty_node_id, required), + (6, funding_outpoint, 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 // data via `write_tlv_fields`. @@ -1471,6 +1525,28 @@ impl MaybeReadable for Event { }; f() }, + 31u8 => { + let f = || { + let mut channel_id = [0; 32]; + let mut user_channel_id: u128 = 0; + let mut counterparty_node_id = RequiredWrapper(None); + let mut funding_outpoint = RequiredWrapper(None); + read_tlv_fields!(reader, { + (0, channel_id, required), + (2, user_channel_id, required), + (4, counterparty_node_id, required), + (6, funding_outpoint, required), + }); + + Ok(Some(Event::PendingChannelReady { + channel_id, + user_channel_id, + counterparty_node_id: counterparty_node_id.0.unwrap(), + funding_outpoint: funding_outpoint.0.unwrap() + })) + }; + f() + }, // 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.