diff --git a/lightning/src/events/mod.rs b/lightning/src/events/mod.rs index c16305bcca0..632d3a39af4 100644 --- a/lightning/src/events/mod.rs +++ b/lightning/src/events/mod.rs @@ -1353,10 +1353,13 @@ pub enum Event { /// Will be `None` for channels created prior to LDK version 0.0.122. channel_type: Option, }, - /// 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 - /// establishment. + /// Used to indicate that a channel with the given `channel_id` is ready to be used. This event + /// is emitted when + /// - the initial funding transaction has been confirmed on-chain to an acceptable depth + /// according to both parties (i.e., `channel_ready` messages were exchanged), + /// - a splice funding transaction has been confirmed on-chain to an acceptable depth according + /// to both parties (i.e., `splice_locked` messages were exchanged), or, + /// - in case of a 0conf channel, when both parties have confirmed the channel establishment. /// /// # Failure Behavior and Persistence /// This event will eventually be replayed after failures-to-handle (i.e., the event handler @@ -1375,6 +1378,11 @@ pub enum Event { user_channel_id: u128, /// The `node_id` of the channel counterparty. counterparty_node_id: PublicKey, + /// The outpoint of the channel's funding transaction. + /// + /// Will be `None` if the channel's funding transaction reached an acceptable depth prior to + /// version 0.2. + funding_txo: Option, /// The features that this channel will operate with. channel_type: ChannelTypeFeatures, }, @@ -1926,11 +1934,13 @@ impl Writeable for Event { ref channel_id, ref user_channel_id, ref counterparty_node_id, + ref funding_txo, ref channel_type, } => { 29u8.write(writer)?; write_tlv_fields!(writer, { (0, channel_id, required), + (1, funding_txo, option), (2, user_channel_id, required), (4, counterparty_node_id, required), (6, channel_type, required), @@ -2437,9 +2447,11 @@ impl MaybeReadable for Event { let mut channel_id = ChannelId::new_zero(); let mut user_channel_id: u128 = 0; let mut counterparty_node_id = RequiredWrapper(None); + let mut funding_txo = None; let mut channel_type = RequiredWrapper(None); read_tlv_fields!(reader, { (0, channel_id, required), + (1, funding_txo, option), (2, user_channel_id, required), (4, counterparty_node_id, required), (6, channel_type, required), @@ -2449,6 +2461,7 @@ impl MaybeReadable for Event { channel_id, user_channel_id, counterparty_node_id: counterparty_node_id.0.unwrap(), + funding_txo, channel_type: channel_type.0.unwrap(), })) }; diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index bc4d38beeea..c3096c07efb 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -38,7 +38,7 @@ use crate::ln::msgs; use crate::ln::msgs::{ClosingSigned, ClosingSignedFeeRange, DecodeError, OnionErrorPacket}; use crate::ln::script::{self, ShutdownScript}; use crate::ln::channel_state::{ChannelShutdownState, CounterpartyForwardingInfo, InboundHTLCDetails, InboundHTLCStateDetails, OutboundHTLCDetails, OutboundHTLCStateDetails}; -use crate::ln::channelmanager::{self, OpenChannelMessage, PendingHTLCStatus, HTLCSource, SentHTLCId, HTLCFailureMsg, PendingHTLCInfo, RAACommitmentOrder, PaymentClaimDetails, BREAKDOWN_TIMEOUT, MIN_CLTV_EXPIRY_DELTA, MAX_LOCAL_BREAKDOWN_TIMEOUT}; +use crate::ln::channelmanager::{self, FundingConfirmedMessage, OpenChannelMessage, PendingHTLCStatus, HTLCSource, SentHTLCId, HTLCFailureMsg, PendingHTLCInfo, RAACommitmentOrder, PaymentClaimDetails, BREAKDOWN_TIMEOUT, MIN_CLTV_EXPIRY_DELTA, MAX_LOCAL_BREAKDOWN_TIMEOUT}; use crate::ln::chan_utils::{ CounterpartyCommitmentSecrets, HTLCOutputInCommitment, htlc_success_tx_weight, htlc_timeout_tx_weight, ChannelPublicKeys, CommitmentTransaction, @@ -1668,6 +1668,10 @@ impl Channel where ChannelPhase::UnfundedV2(chan) => chan.context.get_available_balances_for_scope(&chan.funding, fee_estimator), } } + + pub fn minimum_depth(&self) -> Option { + self.context().minimum_depth(self.funding()) + } } impl From> for Channel @@ -1782,6 +1786,10 @@ pub(super) struct FundingScope { /// The transaction which funds this channel. Note that for manually-funded channels (i.e., /// [`ChannelContext::is_manual_broadcast`] is true) this will be a dummy empty transaction. funding_transaction: Option, + /// The hash of the block in which the funding transaction was included. + funding_tx_confirmed_in: Option, + funding_tx_confirmation_height: u32, + short_channel_id: Option, } impl Writeable for FundingScope { @@ -1792,6 +1800,9 @@ impl Writeable for FundingScope { (5, self.holder_selected_channel_reserve_satoshis, required), (7, self.channel_transaction_parameters, (required: ReadableArgs, None)), (9, self.funding_transaction, option), + (11, self.funding_tx_confirmed_in, option), + (13, self.funding_tx_confirmation_height, required), + (15, self.short_channel_id, option), }); Ok(()) } @@ -1804,6 +1815,9 @@ impl Readable for FundingScope { let mut holder_selected_channel_reserve_satoshis = RequiredWrapper(None); let mut channel_transaction_parameters = RequiredWrapper(None); let mut funding_transaction = None; + let mut funding_tx_confirmed_in = None; + let mut funding_tx_confirmation_height = RequiredWrapper(None); + let mut short_channel_id = None; read_tlv_fields!(reader, { (1, value_to_self_msat, required), @@ -1811,6 +1825,9 @@ impl Readable for FundingScope { (5, holder_selected_channel_reserve_satoshis, required), (7, channel_transaction_parameters, (required: ReadableArgs, None)), (9, funding_transaction, option), + (11, funding_tx_confirmed_in, option), + (13, funding_tx_confirmation_height, required), + (15, short_channel_id, option), }); Ok(Self { @@ -1823,6 +1840,9 @@ impl Readable for FundingScope { counterparty_max_commitment_tx_output: Mutex::new((0, 0)), channel_transaction_parameters: channel_transaction_parameters.0.unwrap(), funding_transaction, + funding_tx_confirmed_in, + funding_tx_confirmation_height: funding_tx_confirmation_height.0.unwrap(), + short_channel_id, #[cfg(any(test, fuzzing))] next_local_commitment_tx_fee_info_cached: Mutex::new(None), #[cfg(any(test, fuzzing))] @@ -1864,6 +1884,10 @@ impl FundingScope { self.channel_transaction_parameters.funding_outpoint } + fn get_funding_txid(&self) -> Option { + self.channel_transaction_parameters.funding_outpoint.map(|txo| txo.txid) + } + fn get_holder_selected_contest_delay(&self) -> u16 { self.channel_transaction_parameters.holder_selected_contest_delay } @@ -1896,12 +1920,69 @@ impl FundingScope { pub fn get_channel_type(&self) -> &ChannelTypeFeatures { &self.channel_transaction_parameters.channel_type_features } + + /// Returns the height in which our funding transaction was confirmed. + pub fn get_funding_tx_confirmation_height(&self) -> Option { + let conf_height = self.funding_tx_confirmation_height; + if conf_height > 0 { + Some(conf_height) + } else { + None + } + } + + /// Returns the current number of confirmations on the funding transaction. + pub fn get_funding_tx_confirmations(&self, height: u32) -> u32 { + if self.funding_tx_confirmation_height == 0 { + // We either haven't seen any confirmation yet, or observed a reorg. + return 0; + } + + height.checked_sub(self.funding_tx_confirmation_height).map_or(0, |c| c + 1) + } + + /// Gets the channel's `short_channel_id`. + /// + /// Will return `None` if the funding hasn't been confirmed yet. + pub fn get_short_channel_id(&self) -> Option { + self.short_channel_id + } } /// Info about a pending splice, used in the pre-splice channel #[cfg(splicing)] struct PendingSplice { pub our_funding_contribution: i64, + + /// The funding txid used in the `splice_locked` sent to the counterparty. + sent_funding_txid: Option, + + /// The funding txid used in the `splice_locked` received from the counterparty. + received_funding_txid: Option, +} + +/// Wrapper around a [`Transaction`] useful for caching the result of [`Transaction::compute_txid`]. +struct ConfirmedTransaction<'a> { + tx: &'a Transaction, + txid: Option, +} + +impl<'a> ConfirmedTransaction<'a> { + /// Returns the underlying [`Transaction`]. + pub fn tx(&self) -> &'a Transaction { + self.tx + } + + /// Returns the [`Txid`], computing and caching it if necessary. + pub fn txid(&mut self) -> Txid { + *self.txid.get_or_insert_with(|| self.tx.compute_txid()) + } +} + +impl<'a> From<&'a Transaction> for ConfirmedTransaction<'a> { + fn from(tx: &'a Transaction) -> Self { + ConfirmedTransaction { tx, txid: None } + } } /// Contains everything about the channel including state, and various flags. @@ -2055,10 +2136,6 @@ pub(super) struct ChannelContext where SP::Target: SignerProvider { /// milliseconds, so any accidental force-closes here should be exceedingly rare. expecting_peer_commitment_signed: bool, - /// The hash of the block in which the funding transaction was included. - funding_tx_confirmed_in: Option, - funding_tx_confirmation_height: u32, - short_channel_id: Option, /// Either the height at which this channel was created or the height at which it was last /// serialized if it was serialized by versions prior to 0.0.103. /// We use this to close if funding is never broadcasted. @@ -2156,14 +2233,18 @@ pub(super) struct ChannelContext where SP::Target: SignerProvider { // blinded paths instead of simple scid+node_id aliases. outbound_scid_alias: u64, + /// Short channel ids used by any prior FundingScope. These are maintained such that + /// ChannelManager can look up the channel for any pending HTLCs. + pub historical_scids: Vec, + // 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, + // We track whether we already emitted an initial `ChannelReady` event. + initial_channel_ready_event_emitted: bool, /// Some if we initiated to shut down the channel. local_initiated_shutdown: Option<()>, @@ -2867,6 +2948,9 @@ impl ChannelContext where SP::Target: SignerProvider { channel_value_satoshis, }, funding_transaction: None, + funding_tx_confirmed_in: None, + funding_tx_confirmation_height: 0, + short_channel_id: None, }; let channel_context = ChannelContext { user_id, @@ -2930,9 +3014,6 @@ impl ChannelContext where SP::Target: SignerProvider { closing_fee_limits: None, target_closing_feerate_sats_per_kw: None, - funding_tx_confirmed_in: None, - funding_tx_confirmation_height: 0, - short_channel_id: None, channel_creation_height: current_chain_height, feerate_per_kw: open_channel_fields.commitment_feerate_sat_per_1000_weight, @@ -2968,10 +3049,11 @@ impl ChannelContext where SP::Target: SignerProvider { latest_inbound_scid_alias: None, outbound_scid_alias: 0, + historical_scids: Vec::new(), channel_pending_event_emitted: false, funding_tx_broadcast_safe_event_emitted: false, - channel_ready_event_emitted: false, + initial_channel_ready_event_emitted: false, channel_keys_id, @@ -3103,6 +3185,9 @@ impl ChannelContext where SP::Target: SignerProvider { channel_value_satoshis, }, funding_transaction: None, + funding_tx_confirmed_in: None, + funding_tx_confirmation_height: 0, + short_channel_id: None, }; let channel_context = Self { user_id, @@ -3164,9 +3249,6 @@ impl ChannelContext where SP::Target: SignerProvider { closing_fee_limits: None, target_closing_feerate_sats_per_kw: None, - funding_tx_confirmed_in: None, - funding_tx_confirmation_height: 0, - short_channel_id: None, channel_creation_height: current_chain_height, feerate_per_kw: commitment_feerate, @@ -3204,10 +3286,11 @@ impl ChannelContext where SP::Target: SignerProvider { latest_inbound_scid_alias: None, outbound_scid_alias, + historical_scids: Vec::new(), channel_pending_event_emitted: false, funding_tx_broadcast_safe_event_emitted: false, - channel_ready_event_emitted: false, + initial_channel_ready_event_emitted: false, channel_keys_id, @@ -3376,8 +3459,22 @@ impl ChannelContext where SP::Target: SignerProvider { self.temporary_channel_id } - pub fn minimum_depth(&self) -> Option { - self.minimum_depth + pub(super) fn minimum_depth(&self, funding: &FundingScope) -> Option { + let minimum_depth = self.minimum_depth?; + + let is_coinbase = funding + .funding_transaction + .as_ref() + .map(|tx| tx.is_coinbase()) + .unwrap_or(false); + + // If the funding transaction is a coinbase transaction, we need 100 confirmations unless + // the channel is 0-conf. + if is_coinbase && minimum_depth > 0 && minimum_depth < COINBASE_MATURITY { + Some(COINBASE_MATURITY) + } else { + Some(minimum_depth) + } } /// Gets the "user_id" value passed into the construction of this channel. It has no special @@ -3386,13 +3483,6 @@ impl ChannelContext where SP::Target: SignerProvider { self.user_id } - /// Gets the channel's `short_channel_id`. - /// - /// Will return `None` if the channel hasn't been confirmed yet. - pub fn get_short_channel_id(&self) -> Option { - self.short_channel_id - } - /// Allowed in any state (including after shutdown) pub fn latest_inbound_scid_alias(&self) -> Option { self.latest_inbound_scid_alias @@ -3417,16 +3507,6 @@ impl ChannelContext where SP::Target: SignerProvider { self.outbound_scid_alias = outbound_scid_alias; } - /// Returns the height in which our funding transaction was confirmed. - pub fn get_funding_tx_confirmation_height(&self) -> Option { - let conf_height = self.funding_tx_confirmation_height; - if conf_height > 0 { - Some(conf_height) - } else { - None - } - } - /// Performs checks against necessary constraints after receiving either an `accept_channel` or /// `accept_channel2` message. pub fn do_accept_channel_checks( @@ -3567,21 +3647,6 @@ impl ChannelContext where SP::Target: SignerProvider { Ok(()) } - /// Returns the block hash in which our funding transaction was confirmed. - pub fn get_funding_tx_confirmed_in(&self) -> Option { - self.funding_tx_confirmed_in - } - - /// Returns the current number of confirmations on the funding transaction. - pub fn get_funding_tx_confirmations(&self, height: u32) -> u32 { - if self.funding_tx_confirmation_height == 0 { - // We either haven't seen any confirmation yet, or observed a reorg. - return 0; - } - - height.checked_sub(self.funding_tx_confirmation_height).map_or(0, |c| c + 1) - } - /// Allowed in any state (including after shutdown) pub fn get_counterparty_node_id(&self) -> PublicKey { self.counterparty_node_id @@ -3659,14 +3724,14 @@ impl ChannelContext where SP::Target: SignerProvider { self.channel_pending_event_emitted = true; } - // Checks whether we should emit a `ChannelReady` event. - pub(crate) fn should_emit_channel_ready_event(&mut self) -> bool { - self.is_usable() && !self.channel_ready_event_emitted + // Checks whether we should emit an initial `ChannelReady` event. + pub(crate) fn should_emit_initial_channel_ready_event(&mut self) -> bool { + self.is_usable() && !self.initial_channel_ready_event_emitted } // Remembers that we already emitted a `ChannelReady` event. - pub(crate) fn set_channel_ready_event_emitted(&mut self) { - self.channel_ready_event_emitted = true; + pub(crate) fn set_initial_channel_ready_event_emitted(&mut self) { + self.initial_channel_ready_event_emitted = true; } // Remembers that we already emitted a `FundingTxBroadcastSafe` event. @@ -4961,6 +5026,102 @@ impl ChannelContext where SP::Target: SignerProvider { self.counterparty_cur_commitment_point = Some(counterparty_cur_commitment_point_override); self.get_initial_counterparty_commitment_signature(funding, logger) } + + fn check_for_funding_tx_confirmed( + &mut self, funding: &mut FundingScope, block_hash: &BlockHash, height: u32, + index_in_block: usize, tx: &mut ConfirmedTransaction, + ) -> Result { + let funding_txo = match funding.get_funding_txo() { + Some(funding_txo) => funding_txo, + None => { + debug_assert!(false); + return Ok(false); + }, + }; + + // 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 funding.funding_tx_confirmation_height == 0 { + if tx.txid() == funding_txo.txid { + let tx = tx.tx(); + let txo_idx = funding_txo.index as usize; + if txo_idx >= tx.output.len() || tx.output[txo_idx].script_pubkey != funding.get_funding_redeemscript().to_p2wsh() || + tx.output[txo_idx].value.to_sat() != funding.get_value_satoshis() { + if funding.is_outbound() { + // If we generated the funding transaction and it doesn't match what it + // should, the client is really broken and we should just panic and + // tell them off. That said, because hash collisions happen with high + // probability in fuzzing mode, if we're fuzzing we just close the + // channel and move on. + #[cfg(not(fuzzing))] + panic!("Client called ChannelManager::funding_transaction_generated with bogus transaction!"); + } + self.update_time_counter += 1; + let err_reason = "funding tx had wrong script/value or output index"; + return Err(ClosureReason::ProcessingError { err: err_reason.to_owned() }); + } else { + if funding.is_outbound() { + if !tx.is_coinbase() { + for input in tx.input.iter() { + if input.witness.is_empty() { + // We generated a malleable funding transaction, implying we've + // just exposed ourselves to funds loss to our counterparty. + #[cfg(not(fuzzing))] + panic!("Client called ChannelManager::funding_transaction_generated with bogus transaction!"); + } + } + } + } + + // The acceptor of v1-established channels doesn't have the funding + // transaction until it is seen on chain. Set it so that minimum_depth + // checks can tell if the coinbase transaction was used. + if funding.funding_transaction.is_none() { + funding.funding_transaction = Some(tx.clone()); + } + + funding.funding_tx_confirmation_height = height; + funding.funding_tx_confirmed_in = Some(*block_hash); + funding.short_channel_id = match scid_from_parts(height as u64, index_in_block as u64, txo_idx as u64) { + Ok(scid) => Some(scid), + Err(_) => panic!("Block was bogus - either height was > 16 million, had > 16 million transactions, or had > 65k outputs"), + }; + + return Ok(true); + } + } + } + + Ok(false) + } + + fn check_for_funding_tx_spent( + &mut self, funding: &FundingScope, tx: &Transaction, logger: &L, + ) -> Result<(), ClosureReason> + where + L::Target: Logger + { + let funding_txo = match funding.get_funding_txo() { + Some(funding_txo) => funding_txo, + None => { + debug_assert!(false); + return Ok(()); + }, + }; + + for input in tx.input.iter() { + if input.previous_output == funding_txo.into_bitcoin_outpoint() { + log_info!( + logger, "Detected channel-closing tx {} spending {}:{}, closing channel {}", + tx.compute_txid(), input.previous_output.txid, input.previous_output.vout, + &self.channel_id(), + ); + return Err(ClosureReason::CommitmentTxConfirmed); + } + } + + Ok(()) + } } // Internal utility functions for channels @@ -5141,6 +5302,19 @@ pub(super) struct FundedChannel where SP::Target: SignerProvider { pending_splice: Option, } +#[cfg(splicing)] +macro_rules! promote_splice_funding { + ($self: expr, $funding: expr) => { + if let Some(scid) = $self.funding.short_channel_id { + $self.context.historical_scids.push(scid); + } + core::mem::swap(&mut $self.funding, $funding); + $self.pending_splice = None; + $self.pending_funding.clear(); + $self.context.announcement_sigs_state = AnnouncementSigsState::NotSent; + } +} + #[cfg(any(test, fuzzing))] struct CommitmentTxInfoCached { fee: u64, @@ -5642,7 +5816,7 @@ impl FundedChannel where } if let Some(scid_alias) = msg.short_channel_id_alias { - if Some(scid_alias) != self.context.short_channel_id { + if Some(scid_alias) != self.funding.short_channel_id { // The scid alias provided can be used to route payments *from* our counterparty, // i.e. can be used for inbound payments and provided in invoices, but is not used // when routing outbound payments. @@ -6842,7 +7016,7 @@ impl FundedChannel where matches!(self.context.channel_state, ChannelState::ChannelReady(_))) { // Broadcast only if not yet confirmed - if self.context.get_funding_tx_confirmation_height().is_none() { + if self.funding.get_funding_tx_confirmation_height().is_none() { funding_broadcastable = Some(funding_transaction.clone()) } } @@ -8175,16 +8349,7 @@ impl FundedChannel where // Called: // * always when a new block/transactions are confirmed with the new height // * when funding is signed with a height of 0 - if self.context.funding_tx_confirmation_height == 0 && self.context.minimum_depth != Some(0) { - return None; - } - - let funding_tx_confirmations = height as i64 - self.context.funding_tx_confirmation_height as i64 + 1; - if funding_tx_confirmations <= 0 { - self.context.funding_tx_confirmation_height = 0; - } - - if funding_tx_confirmations < self.context.minimum_depth.unwrap_or(0) as i64 { + if !self.check_funding_meets_minimum_depth(&self.funding, height) { return None; } @@ -8201,7 +8366,7 @@ impl FundedChannel where // We got a reorg but not enough to trigger a force close, just ignore. false } else { - if self.context.funding_tx_confirmation_height != 0 && + if self.funding.funding_tx_confirmation_height != 0 && self.context.channel_state < ChannelState::ChannelReady(ChannelReadyFlags::new()) { // We should never see a funding transaction on-chain until we've received @@ -8253,85 +8418,166 @@ impl FundedChannel where } } + #[cfg(splicing)] + fn check_get_splice_locked( + &self, pending_splice: &PendingSplice, funding: &FundingScope, height: u32, + ) -> Option { + if !self.check_funding_meets_minimum_depth(funding, height) { + return None; + } + + let confirmed_funding_txid = match funding.get_funding_txid() { + Some(funding_txid) => funding_txid, + None => { + debug_assert!(false); + return None; + }, + }; + + match pending_splice.sent_funding_txid { + Some(sent_funding_txid) if confirmed_funding_txid == sent_funding_txid => { + debug_assert!(false); + None + }, + _ => { + Some(msgs::SpliceLocked { + channel_id: self.context.channel_id(), + splice_txid: confirmed_funding_txid, + }) + }, + } + } + + fn check_funding_meets_minimum_depth(&self, funding: &FundingScope, height: u32) -> bool { + let minimum_depth = self.context.minimum_depth(funding) + .expect("ChannelContext::minimum_depth should be set for FundedChannel"); + + // Zero-conf channels always meet the minimum depth. + if minimum_depth == 0 { + return true; + } + + if funding.funding_tx_confirmation_height == 0 { + return false; + } + + let funding_tx_confirmations = height as i64 - funding.funding_tx_confirmation_height as i64 + 1; + if funding_tx_confirmations < minimum_depth as i64 { + return false; + } + + return true; + } + + fn maybe_promote_splice_funding( + &mut self, splice_txid: Txid, confirmed_funding_index: usize, + ) -> bool { + debug_assert!(self.pending_splice.is_some()); + debug_assert!(confirmed_funding_index < self.pending_funding.len()); + + let pending_splice = self.pending_splice.as_mut().unwrap(); + pending_splice.sent_funding_txid = Some(splice_txid); + + if pending_splice.sent_funding_txid == pending_splice.received_funding_txid { + let funding = self.pending_funding.get_mut(confirmed_funding_index).unwrap(); + promote_splice_funding!(self, funding); + return true; + } + + return false; + } + /// When a transaction is confirmed, we check whether it is or spends the funding transaction /// In the first case, we store the confirmation height and calculating the short channel id. /// In the second, we simply return an Err indicating we need to be force-closed now. pub fn transactions_confirmed( &mut self, block_hash: &BlockHash, height: u32, txdata: &TransactionData, chain_hash: ChainHash, node_signer: &NS, user_config: &UserConfig, logger: &L - ) -> Result<(Option, Option), ClosureReason> + ) -> Result<(Option, Option), ClosureReason> where NS::Target: NodeSigner, L::Target: Logger { - let mut msgs = (None, None); - if let Some(funding_txo) = self.funding.get_funding_txo() { - for &(index_in_block, tx) in txdata.iter() { - // 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.context.funding_tx_confirmation_height == 0 { - if tx.compute_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.funding.get_funding_redeemscript().to_p2wsh() || - tx.output[txo_idx].value.to_sat() != self.funding.get_value_satoshis() { - if self.funding.is_outbound() { - // If we generated the funding transaction and it doesn't match what it - // should, the client is really broken and we should just panic and - // tell them off. That said, because hash collisions happen with high - // probability in fuzzing mode, if we're fuzzing we just close the - // channel and move on. - #[cfg(not(fuzzing))] - panic!("Client called ChannelManager::funding_transaction_generated with bogus transaction!"); - } - self.context.update_time_counter += 1; - let err_reason = "funding tx had wrong script/value or output index"; - return Err(ClosureReason::ProcessingError { err: err_reason.to_owned() }); - } else { - if self.funding.is_outbound() { - if !tx.is_coinbase() { - for input in tx.input.iter() { - if input.witness.is_empty() { - // We generated a malleable funding transaction, implying we've - // just exposed ourselves to funds loss to our counterparty. - #[cfg(not(fuzzing))] - panic!("Client called ChannelManager::funding_transaction_generated with bogus transaction!"); - } - } - } - } - self.context.funding_tx_confirmation_height = height; - self.context.funding_tx_confirmed_in = Some(*block_hash); - self.context.short_channel_id = match scid_from_parts(height as u64, index_in_block as u64, txo_idx as u64) { - Ok(scid) => Some(scid), - Err(_) => panic!("Block was bogus - either height was > 16 million, had > 16 million transactions, or had > 65k outputs"), - } - } - // If this is a coinbase transaction and not a 0-conf channel - // we should update our min_depth to 100 to handle coinbase maturity - if tx.is_coinbase() && - self.context.minimum_depth.unwrap_or(0) > 0 && - self.context.minimum_depth.unwrap_or(0) < COINBASE_MATURITY { - self.context.minimum_depth = Some(COINBASE_MATURITY); + for &(index_in_block, tx) in txdata.iter() { + let mut confirmed_tx = ConfirmedTransaction::from(tx); + + // If we allow 1-conf funding, we may need to check for channel_ready or splice_locked here + // and send it immediately instead of waiting for a best_block_updated call (which may have + // already happened for this block). + let is_funding_tx_confirmed = self.context.check_for_funding_tx_confirmed( + &mut self.funding, block_hash, height, index_in_block, &mut confirmed_tx, + )?; + + if is_funding_tx_confirmed { + if let Some(channel_ready) = self.check_get_channel_ready(height, logger) { + for &(idx, tx) in txdata.iter() { + if idx > index_in_block { + self.context.check_for_funding_tx_spent(&self.funding, tx, logger)?; } } - // If we allow 1-conf funding, we may need to check for channel_ready here and - // send it immediately instead of waiting for a best_block_updated call (which - // may have already happened for this block). - if let Some(channel_ready) = self.check_get_channel_ready(height, logger) { - log_info!(logger, "Sending a channel_ready to our peer for channel {}", &self.context.channel_id); - let announcement_sigs = self.get_announcement_sigs(node_signer, chain_hash, user_config, height, logger); - msgs = (Some(channel_ready), announcement_sigs); + + log_info!(logger, "Sending a channel_ready to our peer for channel {}", &self.context.channel_id); + let announcement_sigs = self.get_announcement_sigs(node_signer, chain_hash, user_config, height, logger); + return Ok((Some(FundingConfirmedMessage::Establishment(channel_ready)), announcement_sigs)); + } + } + + #[cfg(splicing)] + let mut confirmed_funding_index = None; + #[cfg(splicing)] + for (index, funding) in self.pending_funding.iter_mut().enumerate() { + if self.context.check_for_funding_tx_confirmed( + funding, block_hash, height, index_in_block, &mut confirmed_tx, + )? { + if confirmed_funding_index.is_some() { + let err_reason = "splice tx of another pending funding already confirmed"; + return Err(ClosureReason::ProcessingError { err: err_reason.to_owned() }); } + + confirmed_funding_index = Some(index); } - for inp in tx.input.iter() { - if inp.previous_output == funding_txo.into_bitcoin_outpoint() { - log_info!(logger, "Detected channel-closing tx {} spending {}:{}, closing channel {}", tx.compute_txid(), inp.previous_output.txid, inp.previous_output.vout, &self.context.channel_id()); - return Err(ClosureReason::CommitmentTxConfirmed); + } + + #[cfg(splicing)] + if let Some(confirmed_funding_index) = confirmed_funding_index { + let pending_splice = match self.pending_splice.as_ref() { + Some(pending_splice) => pending_splice, + None => { + // TODO: Move pending_funding into pending_splice + debug_assert!(false); + let err = "expected a pending splice".to_string(); + return Err(ClosureReason::ProcessingError { err }); + }, + }; + let funding = self.pending_funding.get(confirmed_funding_index).unwrap(); + + if let Some(splice_locked) = self.check_get_splice_locked(pending_splice, funding, height) { + for &(idx, tx) in txdata.iter() { + if idx > index_in_block { + self.context.check_for_funding_tx_spent(funding, tx, logger)?; + } } + + log_info!(logger, "Sending a splice_locked to our peer for channel {}", &self.context.channel_id); + + let announcement_sigs = self + .maybe_promote_splice_funding(splice_locked.splice_txid, confirmed_funding_index) + .then(|| self.get_announcement_sigs(node_signer, chain_hash, user_config, height, logger)) + .flatten(); + + return Ok((Some(FundingConfirmedMessage::Splice(splice_locked)), announcement_sigs)); } } + + self.context.check_for_funding_tx_spent(&self.funding, tx, logger)?; + #[cfg(splicing)] + for funding in self.pending_funding.iter() { + self.context.check_for_funding_tx_spent(funding, tx, logger)?; + } + } - Ok(msgs) + + Ok((None, None)) } /// When a new block is connected, we check the height of the block against outbound holding @@ -8348,7 +8594,7 @@ impl FundedChannel where pub fn best_block_updated( &mut self, height: u32, highest_header_time: u32, chain_hash: ChainHash, node_signer: &NS, user_config: &UserConfig, logger: &L - ) -> Result<(Option, Vec<(HTLCSource, PaymentHash)>, Option), ClosureReason> + ) -> Result<(Option, Vec<(HTLCSource, PaymentHash)>, Option), ClosureReason> where NS::Target: NodeSigner, L::Target: Logger @@ -8359,7 +8605,7 @@ impl FundedChannel where fn do_best_block_updated( &mut self, height: u32, highest_header_time: u32, chain_node_signer: Option<(ChainHash, &NS, &UserConfig)>, logger: &L - ) -> Result<(Option, Vec<(HTLCSource, PaymentHash)>, Option), ClosureReason> + ) -> Result<(Option, Vec<(HTLCSource, PaymentHash)>, Option), ClosureReason> where NS::Target: NodeSigner, L::Target: Logger @@ -8388,15 +8634,15 @@ impl FundedChannel where self.get_announcement_sigs(node_signer, chain_hash, user_config, height, logger) } else { None }; log_info!(logger, "Sending a channel_ready to our peer for channel {}", &self.context.channel_id); - return Ok((Some(channel_ready), timed_out_htlcs, announcement_sigs)); + return Ok((Some(FundingConfirmedMessage::Establishment(channel_ready)), timed_out_htlcs, announcement_sigs)); } if matches!(self.context.channel_state, ChannelState::ChannelReady(_)) || self.context.channel_state.is_our_channel_ready() { - let mut funding_tx_confirmations = height as i64 - self.context.funding_tx_confirmation_height as i64 + 1; - if self.context.funding_tx_confirmation_height == 0 { - // Note that check_get_channel_ready may reset funding_tx_confirmation_height to - // zero if it has been reorged out, however in either case, our state flags + let mut funding_tx_confirmations = height as i64 - self.funding.funding_tx_confirmation_height as i64 + 1; + if self.funding.funding_tx_confirmation_height == 0 { + // Note that transaction_unconfirmed may have reset funding_tx_confirmation_height + // to zero if it has been reorged out, however in either case, our state flags // indicate we've already sent a channel_ready funding_tx_confirmations = 0; } @@ -8410,12 +8656,12 @@ impl FundedChannel where // 0-conf channel, but not doing so may lead to the // `ChannelManager::short_to_chan_info` map being inconsistent, so we currently have // to. - if funding_tx_confirmations == 0 && self.context.funding_tx_confirmed_in.is_some() { + if funding_tx_confirmations == 0 && self.funding.funding_tx_confirmed_in.is_some() { let err_reason = format!("Funding transaction was un-confirmed. Locked at {} confs, now have {} confs.", self.context.minimum_depth.unwrap(), funding_tx_confirmations); return Err(ClosureReason::ProcessingError { err: err_reason }); } - } else if !self.funding.is_outbound() && self.context.funding_tx_confirmed_in.is_none() && + } else if !self.funding.is_outbound() && self.funding.funding_tx_confirmed_in.is_none() && height >= self.context.channel_creation_height + FUNDING_CONF_DEADLINE_BLOCKS { log_info!(logger, "Closing channel {} due to funding timeout", &self.context.channel_id); // If funding_tx_confirmed_in is unset, the channel must not be active @@ -8424,35 +8670,114 @@ impl FundedChannel where return Err(ClosureReason::FundingTimedOut); } + #[cfg(splicing)] + let mut confirmed_funding_index = None; + #[cfg(splicing)] + for (index, funding) in self.pending_funding.iter().enumerate() { + if confirmed_funding_index.is_some() { + let err_reason = "splice tx of another pending funding already confirmed"; + return Err(ClosureReason::ProcessingError { err: err_reason.to_owned() }); + } + + if funding.funding_tx_confirmed_in.is_some() { + confirmed_funding_index = Some(index); + } + } + + #[cfg(splicing)] + if let Some(confirmed_funding_index) = confirmed_funding_index { + let pending_splice = match self.pending_splice.as_ref() { + Some(pending_splice) => pending_splice, + None => { + // TODO: Move pending_funding into pending_splice + debug_assert!(false); + let err = "expected a pending splice".to_string(); + return Err(ClosureReason::ProcessingError { err }); + }, + }; + let funding = self.pending_funding.get(confirmed_funding_index).unwrap(); + + if let Some(splice_locked) = self.check_get_splice_locked(pending_splice, funding, height) { + log_info!(logger, "Sending a splice_locked to our peer for channel {}", &self.context.channel_id); + + let announcement_sigs = self + .maybe_promote_splice_funding(splice_locked.splice_txid, confirmed_funding_index) + .then(|| chain_node_signer + .and_then(|(chain_hash, node_signer, user_config)| + self.get_announcement_sigs(node_signer, chain_hash, user_config, height, logger) + ) + ) + .flatten(); + + return Ok((Some(FundingConfirmedMessage::Splice(splice_locked)), timed_out_htlcs, announcement_sigs)); + } + } + let announcement_sigs = if let Some((chain_hash, node_signer, user_config)) = chain_node_signer { self.get_announcement_sigs(node_signer, chain_hash, user_config, height, logger) } else { None }; Ok((None, timed_out_htlcs, announcement_sigs)) } - /// Indicates the funding transaction is no longer confirmed in the main chain. This may + pub fn get_relevant_txids(&self) -> impl Iterator)> + '_ { + core::iter::once(&self.funding) + .chain(self.pending_funding.iter()) + .map(|funding| + ( + funding.get_funding_txid(), + funding.get_funding_tx_confirmation_height(), + funding.funding_tx_confirmed_in, + ) + ) + .filter_map(|(txid_opt, height_opt, hash_opt)| + if let (Some(funding_txid), Some(conf_height), Some(block_hash)) = (txid_opt, height_opt, hash_opt) { + Some((funding_txid, conf_height, Some(block_hash))) + } else { + None + } + ) + } + + /// Checks if any funding transaction is no longer confirmed in the main chain. This may /// force-close the channel, but may also indicate a harmless reorganization of a block or two - /// before the channel has reached channel_ready and we can just wait for more blocks. - pub fn funding_transaction_unconfirmed(&mut self, logger: &L) -> Result<(), ClosureReason> where L::Target: Logger { - if self.context.funding_tx_confirmation_height != 0 { - // We handle the funding disconnection by calling best_block_updated with a height one - // below where our funding was connected, implying a reorg back to conf_height - 1. - let reorg_height = self.context.funding_tx_confirmation_height - 1; - // We use the time field to bump the current time we set on channel updates if its - // larger. If we don't know that time has moved forward, we can just set it to the last - // time we saw and it will be ignored. - let best_time = self.context.update_time_counter; - match self.do_best_block_updated(reorg_height, best_time, None::<(ChainHash, &&dyn NodeSigner, &UserConfig)>, logger) { - Ok((channel_ready, timed_out_htlcs, announcement_sigs)) => { - assert!(channel_ready.is_none(), "We can't generate a funding with 0 confirmations?"); - assert!(timed_out_htlcs.is_empty(), "We can't have accepted HTLCs with a timeout before our funding confirmation?"); - assert!(announcement_sigs.is_none(), "We can't generate an announcement_sigs with 0 confirmations?"); - Ok(()) - }, - Err(e) => Err(e) + /// before the channel has reached channel_ready or splice_locked, and we can just wait for more + /// blocks. + pub fn transaction_unconfirmed( + &mut self, txid: &Txid, logger: &L, + ) -> Result<(), ClosureReason> + where + L::Target: Logger, + { + let unconfirmed_funding = core::iter::once(&mut self.funding) + .chain(self.pending_funding.iter_mut()) + .find(|funding| funding.get_funding_txid() == Some(*txid)); + + if let Some(funding) = unconfirmed_funding { + if funding.funding_tx_confirmation_height != 0 { + // We handle the funding disconnection by calling best_block_updated with a height one + // below where our funding was connected, implying a reorg back to conf_height - 1. + let reorg_height = funding.funding_tx_confirmation_height - 1; + // We use the time field to bump the current time we set on channel updates if its + // larger. If we don't know that time has moved forward, we can just set it to the last + // time we saw and it will be ignored. + let best_time = self.context.update_time_counter; + + funding.funding_tx_confirmation_height = 0; + + match self.do_best_block_updated(reorg_height, best_time, None::<(ChainHash, &&dyn NodeSigner, &UserConfig)>, logger) { + Ok((channel_ready, timed_out_htlcs, announcement_sigs)) => { + assert!(channel_ready.is_none(), "We can't generate a funding with 0 confirmations?"); + assert!(timed_out_htlcs.is_empty(), "We can't have accepted HTLCs with a timeout before our funding confirmation?"); + assert!(announcement_sigs.is_none(), "We can't generate an announcement_sigs with 0 confirmations?"); + Ok(()) + }, + Err(e) => Err(e), + } + } else { + // We never learned about the funding confirmation anyway, just ignore + Ok(()) } } else { - // We never learned about the funding confirmation anyway, just ignore Ok(()) } } @@ -8481,7 +8806,7 @@ impl FundedChannel where 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() + let short_channel_id = self.funding.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) .map_err(|_| ChannelError::Ignore("Failed to retrieve own public key".to_owned()))?); @@ -8510,7 +8835,7 @@ impl FundedChannel where NS::Target: NodeSigner, L::Target: Logger { - if self.context.funding_tx_confirmation_height == 0 || self.context.funding_tx_confirmation_height + 5 > best_block_height { + if self.funding.funding_tx_confirmation_height == 0 || self.funding.funding_tx_confirmation_height + 5 > best_block_height { return None; } @@ -8553,7 +8878,7 @@ impl FundedChannel where }, Ok(v) => v }; - let short_channel_id = match self.context.get_short_channel_id() { + let short_channel_id = match self.funding.get_short_channel_id() { Some(scid) => scid, None => return None, }; @@ -8631,7 +8956,7 @@ impl FundedChannel where } self.context.announcement_sigs = Some((msg.node_signature, msg.bitcoin_signature)); - if self.context.funding_tx_confirmation_height == 0 || self.context.funding_tx_confirmation_height + 5 > best_block_height { + if self.funding.funding_tx_confirmation_height == 0 || self.funding.funding_tx_confirmation_height + 5 > best_block_height { return Err(ChannelError::Ignore( "Got announcement_signatures prior to the required six confirmations - we may not have received a block yet that our peer has".to_owned())); } @@ -8644,7 +8969,7 @@ impl FundedChannel where pub fn get_signed_channel_announcement( &self, node_signer: &NS, chain_hash: ChainHash, best_block_height: u32, user_config: &UserConfig ) -> Option where NS::Target: NodeSigner { - if self.context.funding_tx_confirmation_height == 0 || self.context.funding_tx_confirmation_height + 5 > best_block_height { + if self.funding.funding_tx_confirmation_height == 0 || self.funding.funding_tx_confirmation_height + 5 > best_block_height { return None; } let announcement = match self.get_channel_announcement(node_signer, chain_hash, user_config) { @@ -8755,6 +9080,8 @@ impl FundedChannel where self.pending_splice = Some(PendingSplice { our_funding_contribution: our_funding_contribution_satoshis, + sent_funding_txid: None, + received_funding_txid: None, }); let msg = self.get_splice_init(our_funding_contribution_satoshis, funding_feerate_per_kw, locktime); @@ -8843,6 +9170,41 @@ impl FundedChannel where Ok(()) } + #[cfg(splicing)] + pub fn splice_locked( + &mut self, msg: &msgs::SpliceLocked, node_signer: &NS, chain_hash: ChainHash, + user_config: &UserConfig, best_block: &BestBlock, logger: &L, + ) -> Result, ChannelError> + where + NS::Target: NodeSigner, + L::Target: Logger + { + let pending_splice = match self.pending_splice.as_mut() { + Some(pending_splice) => pending_splice, + None => { + return Err(ChannelError::Ignore(format!("Channel is not in pending splice"))); + }, + }; + + if let Some(sent_funding_txid) = pending_splice.sent_funding_txid { + if sent_funding_txid == msg.splice_txid { + if let Some(funding) = self.pending_funding + .iter_mut() + .find(|funding| funding.get_funding_txid() == Some(sent_funding_txid)) + { + promote_splice_funding!(self, funding); + return Ok(self.get_announcement_sigs(node_signer, chain_hash, user_config, best_block.height, logger)); + } + + let err = "unknown splice funding txid"; + return Err(ChannelError::close(err.to_string())); + } + } + + pending_splice.received_funding_txid = Some(msg.splice_txid); + Ok(None) + } + // Send stuff to our remote peers: /// Queues up an outbound HTLC to send by placing it in the holding cell. You should call @@ -9700,14 +10062,6 @@ impl OutboundV1Channel where SP::Target: SignerProvider { self.context.channel_state = ChannelState::FundingNegotiated; self.context.channel_id = ChannelId::v1_from_funding_outpoint(funding_txo); - // If the funding transaction is a coinbase transaction, we need to set the minimum depth to 100. - // We can skip this if it is a zero-conf channel. - if funding_transaction.is_coinbase() && - self.context.minimum_depth.unwrap_or(0) > 0 && - self.context.minimum_depth.unwrap_or(0) < COINBASE_MATURITY { - self.context.minimum_depth = Some(COINBASE_MATURITY); - } - debug_assert!(self.funding.funding_transaction.is_none()); self.funding.funding_transaction = Some(funding_transaction); self.context.is_batch_funding = Some(()).filter(|_| is_batch_funding); @@ -10800,9 +11154,9 @@ impl Writeable for FundedChannel where SP::Target: SignerProvider // consider the stale state on reload. 0u8.write(writer)?; - self.context.funding_tx_confirmed_in.write(writer)?; - self.context.funding_tx_confirmation_height.write(writer)?; - self.context.short_channel_id.write(writer)?; + self.funding.funding_tx_confirmed_in.write(writer)?; + self.funding.funding_tx_confirmation_height.write(writer)?; + self.funding.short_channel_id.write(writer)?; self.context.counterparty_dust_limit_satoshis.write(writer)?; self.context.holder_dust_limit_satoshis.write(writer)?; @@ -10863,7 +11217,7 @@ impl Writeable for FundedChannel where SP::Target: SignerProvider { Some(self.context.holder_max_htlc_value_in_flight_msat) } else { None }; 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 initial_channel_ready_event_emitted = Some(self.context.initial_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 @@ -10907,7 +11261,7 @@ impl Writeable for FundedChannel where SP::Target: SignerProvider (17, self.context.announcement_sigs_state, required), (19, self.context.latest_inbound_scid_alias, option), (21, self.context.outbound_scid_alias, required), - (23, channel_ready_event_emitted, option), + (23, initial_channel_ready_event_emitted, option), (25, user_id_high_opt, option), (27, self.context.channel_keys_id, required), (28, holder_max_accepted_htlcs, option), @@ -10927,6 +11281,7 @@ impl Writeable for FundedChannel where SP::Target: SignerProvider (54, self.pending_funding, optional_vec), // Added in 0.2 (55, removed_htlc_failure_attribution_data, optional_vec), // Added in 0.2 (57, holding_cell_failure_attribution_data, optional_vec), // Added in 0.2 + (58, self.context.historical_scids, optional_vec), // Added in 0.2 }); Ok(()) @@ -11211,7 +11566,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, &'c Channel let mut latest_inbound_scid_alias = None; let mut outbound_scid_alias = 0u64; let mut channel_pending_event_emitted = None; - let mut channel_ready_event_emitted = None; + let mut initial_channel_ready_event_emitted = None; let mut funding_tx_broadcast_safe_event_emitted = None; let mut user_id_high_opt: Option = None; @@ -11242,6 +11597,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, &'c Channel let mut is_manual_broadcast = None; let mut pending_funding = Some(Vec::new()); + let mut historical_scids = Some(Vec::new()); read_tlv_fields!(reader, { (0, announcement_sigs, option), @@ -11261,7 +11617,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, &'c Channel (17, announcement_sigs_state, required), (19, latest_inbound_scid_alias, option), (21, outbound_scid_alias, required), - (23, channel_ready_event_emitted, option), + (23, initial_channel_ready_event_emitted, option), (25, user_id_high_opt, option), (27, channel_keys_id, required), (28, holder_max_accepted_htlcs, option), @@ -11281,6 +11637,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, &'c Channel (54, pending_funding, optional_vec), // Added in 0.2 (55, removed_htlc_failure_attribution_data, optional_vec), (57, holding_cell_failure_attribution_data, optional_vec), + (58, historical_scids, optional_vec), // Added in 0.2 }); let holder_signer = signer_provider.derive_channel_signer(channel_keys_id); @@ -11457,6 +11814,9 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, &'c Channel channel_transaction_parameters: channel_parameters, funding_transaction, + funding_tx_confirmed_in, + funding_tx_confirmation_height, + short_channel_id, }, pending_funding: pending_funding.unwrap(), context: ChannelContext { @@ -11520,9 +11880,6 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, &'c Channel closing_fee_limits: None, target_closing_feerate_sats_per_kw, - funding_tx_confirmed_in, - funding_tx_confirmation_height, - short_channel_id, channel_creation_height, counterparty_dust_limit_satoshis, @@ -11557,10 +11914,11 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, &'c Channel latest_inbound_scid_alias, // Later in the ChannelManager deserialization phase we scan for channels and assign scid aliases if its missing outbound_scid_alias, + historical_scids: historical_scids.unwrap(), 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), + initial_channel_ready_event_emitted: initial_channel_ready_event_emitted.unwrap_or(true), channel_keys_id, diff --git a/lightning/src/ln/channel_state.rs b/lightning/src/ln/channel_state.rs index c941e0eb9d0..c28b4687631 100644 --- a/lightning/src/ln/channel_state.rs +++ b/lightning/src/ln/channel_state.rs @@ -516,7 +516,7 @@ impl ChannelDetails { } else { None }, - short_channel_id: context.get_short_channel_id(), + short_channel_id: funding.get_short_channel_id(), outbound_scid_alias: if context.is_usable() { Some(context.outbound_scid_alias()) } else { @@ -531,8 +531,8 @@ impl ChannelDetails { next_outbound_htlc_limit_msat: balance.next_outbound_htlc_limit_msat, next_outbound_htlc_minimum_msat: balance.next_outbound_htlc_minimum_msat, user_channel_id: context.get_user_id(), - confirmations_required: context.minimum_depth(), - confirmations: Some(context.get_funding_tx_confirmations(best_block_height)), + confirmations_required: channel.minimum_depth(), + confirmations: Some(funding.get_funding_tx_confirmations(best_block_height)), force_close_spend_delay: funding.get_counterparty_selected_contest_delay(), is_outbound: funding.is_outbound(), is_channel_ready: context.is_usable(), diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 170d8261d5a..cecd4636bc2 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -82,7 +82,7 @@ use crate::sign::{EntropySource, NodeSigner, Recipient, SignerProvider}; use crate::sign::ecdsa::EcdsaChannelSigner; use crate::util::config::{ChannelConfig, ChannelConfigUpdate, ChannelConfigOverrides, UserConfig}; use crate::util::wakers::{Future, Notifier}; -use crate::util::scid_utils::fake_scid; +use crate::util::scid_utils::{block_from_scid, fake_scid}; use crate::util::string::UntrustedString; use crate::util::ser::{BigSize, FixedLengthReader, LengthReadable, Readable, ReadableArgs, MaybeReadable, Writeable, Writer, VecWriter}; use crate::util::logger::{Level, Logger, WithContext}; @@ -2910,6 +2910,11 @@ const MAX_NO_CHANNEL_PEERS: usize = 250; /// become invalid over time as channels are closed. Thus, they are only suitable for short-term use. pub const MAX_SHORT_LIVED_RELATIVE_EXPIRY: Duration = Duration::from_secs(60 * 60 * 24); +/// The number of blocks to wait for a channel_announcement to propagate such that payments using an +/// older SCID can still be relayed. Once the spend of the previous funding transaction has reached +/// this number of confirmations, the corresponding SCID will be forgotten. +const CHANNEL_ANNOUNCEMENT_PROPAGATION_DELAY: u32 = 12; + /// Used by [`ChannelManager::list_recent_payments`] to express the status of recent payments. /// These include payments that have yet to find a successful path, or have unresolved HTLCs. #[derive(Debug, PartialEq)] @@ -3040,7 +3045,7 @@ macro_rules! handle_error { /// Note that this step can be skipped if the channel was never opened (through the creation of a /// [`ChannelMonitor`]/channel funding transaction) to begin with. macro_rules! locked_close_channel { - ($self: ident, $peer_state: expr, $channel_context: expr, $shutdown_res_mut: expr) => {{ + ($self: ident, $peer_state: expr, $channel_context: expr, $channel_funding: expr, $shutdown_res_mut: expr) => {{ if let Some((_, funding_txo, _, update)) = $shutdown_res_mut.monitor_update.take() { handle_new_monitor_update!($self, funding_txo, update, $peer_state, $channel_context, REMAIN_LOCKED_UPDATE_ACTIONS_PROCESSED_LATER); @@ -3050,12 +3055,12 @@ macro_rules! locked_close_channel { // into the map (which prevents the `PeerState` from being cleaned up) for channels that // never even got confirmations (which would open us up to DoS attacks). let update_id = $channel_context.get_latest_monitor_update_id(); - if $channel_context.get_funding_tx_confirmation_height().is_some() || $channel_context.minimum_depth() == Some(0) || update_id > 1 { + if $channel_funding.get_funding_tx_confirmation_height().is_some() || $channel_context.minimum_depth($channel_funding) == Some(0) || update_id > 1 { let chan_id = $channel_context.channel_id(); $peer_state.closed_channel_monitor_update_ids.insert(chan_id, update_id); } let mut short_to_chan_info = $self.short_to_chan_info.write().unwrap(); - if let Some(short_id) = $channel_context.get_short_channel_id() { + if let Some(short_id) = $channel_funding.get_short_channel_id() { short_to_chan_info.remove(&short_id); } else { // If the channel was never confirmed on-chain prior to its closure, remove the @@ -3068,6 +3073,9 @@ macro_rules! locked_close_channel { debug_assert!(alias_removed); } short_to_chan_info.remove(&$channel_context.outbound_scid_alias()); + for scid in &$channel_context.historical_scids { + short_to_chan_info.remove(scid); + } }} } @@ -3088,7 +3096,7 @@ macro_rules! convert_channel_err { let logger = WithChannelContext::from(&$self.logger, &$context, None); log_error!(logger, "Closing channel {} due to close-required error: {}", $channel_id, msg); let mut shutdown_res = $context.force_shutdown($funding, true, reason); - locked_close_channel!($self, $peer_state, $context, &mut shutdown_res); + locked_close_channel!($self, $peer_state, $context, $funding, &mut shutdown_res); let err = MsgHandleErrInternal::from_finish_shutdown(msg, *$channel_id, shutdown_res, $channel_update); (true, err) @@ -3153,7 +3161,7 @@ macro_rules! remove_channel_entry { ($self: ident, $peer_state: expr, $entry: expr, $shutdown_res_mut: expr) => { { let channel = $entry.remove_entry().1; - locked_close_channel!($self, $peer_state, &channel.context(), $shutdown_res_mut); + locked_close_channel!($self, $peer_state, &channel.context(), channel.funding(), $shutdown_res_mut); channel } } @@ -3171,13 +3179,20 @@ macro_rules! send_channel_ready { let outbound_alias_insert = short_to_chan_info.insert($channel.context.outbound_scid_alias(), ($channel.context.get_counterparty_node_id(), $channel.context.channel_id())); assert!(outbound_alias_insert.is_none() || outbound_alias_insert.unwrap() == ($channel.context.get_counterparty_node_id(), $channel.context.channel_id()), "SCIDs should never collide - ensure you weren't behind the chain tip by a full month when creating channels"); - if let Some(real_scid) = $channel.context.get_short_channel_id() { - let scid_insert = short_to_chan_info.insert(real_scid, ($channel.context.get_counterparty_node_id(), $channel.context.channel_id())); + insert_short_channel_id!(short_to_chan_info, $channel); + }} +} + +macro_rules! insert_short_channel_id { + ($short_to_chan_info: ident, $channel: expr) => {{ + if let Some(real_scid) = $channel.funding.get_short_channel_id() { + let scid_insert = $short_to_chan_info.insert(real_scid, ($channel.context.get_counterparty_node_id(), $channel.context.channel_id())); assert!(scid_insert.is_none() || scid_insert.unwrap() == ($channel.context.get_counterparty_node_id(), $channel.context.channel_id()), "SCIDs should never collide - ensure you weren't behind the chain tip by a full month when creating channels"); } }} } + 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() { @@ -3210,17 +3225,18 @@ macro_rules! emit_channel_pending_event { } } -macro_rules! emit_channel_ready_event { +macro_rules! emit_initial_channel_ready_event { ($locked_events: expr, $channel: expr) => { - if $channel.context.should_emit_channel_ready_event() { + if $channel.context.should_emit_initial_channel_ready_event() { debug_assert!($channel.context.channel_pending_event_emitted()); $locked_events.push_back((events::Event::ChannelReady { channel_id: $channel.context.channel_id(), user_channel_id: $channel.context.get_user_id(), counterparty_node_id: $channel.context.get_counterparty_node_id(), + funding_txo: $channel.funding.get_funding_txo().map(|outpoint| outpoint.into_bitcoin_outpoint()), channel_type: $channel.funding.get_channel_type().clone(), }, None)); - $channel.context.set_channel_ready_event_emitted(); + $channel.context.set_initial_channel_ready_event_emitted(); } } } @@ -4121,7 +4137,7 @@ where let mut peer_state = peer_state_mutex.lock().unwrap(); if let Some(mut chan) = peer_state.channel_by_id.remove(&channel_id) { let mut close_res = chan.force_shutdown(false, ClosureReason::FundingBatchClosure); - locked_close_channel!(self, &mut *peer_state, chan.context(), close_res); + locked_close_channel!(self, &mut *peer_state, chan.context(), chan.funding(), close_res); shutdown_results.push(close_res); } } @@ -4592,7 +4608,7 @@ where action: msgs::ErrorAction::IgnoreError }); } - if chan.context.get_short_channel_id().is_none() { + if chan.funding.get_short_channel_id().is_none() { return Err(LightningError{err: "Channel not yet established".to_owned(), action: msgs::ErrorAction::IgnoreError}); } let logger = WithChannelContext::from(&self.logger, &chan.context, None); @@ -4614,7 +4630,7 @@ where fn get_channel_update_for_unicast(&self, chan: &FundedChannel) -> Result { let logger = WithChannelContext::from(&self.logger, &chan.context, None); log_trace!(logger, "Attempting to generate channel update for channel {}", chan.context.channel_id()); - let short_channel_id = match chan.context.get_short_channel_id().or(chan.context.latest_inbound_scid_alias()) { + let short_channel_id = match chan.funding.get_short_channel_id().or(chan.context.latest_inbound_scid_alias()) { None => return Err(LightningError{err: "Channel not yet established".to_owned(), action: msgs::ErrorAction::IgnoreError}), Some(id) => id, }; @@ -5510,7 +5526,7 @@ where .map(|(mut chan, mut peer_state)| { let closure_reason = ClosureReason::ProcessingError { err: e.clone() }; let mut close_res = chan.force_shutdown(false, closure_reason); - locked_close_channel!(self, peer_state, chan.context(), close_res); + locked_close_channel!(self, peer_state, chan.context(), chan.funding(), close_res); shutdown_results.push(close_res); peer_state.pending_msg_events.push(MessageSendEvent::HandleError { node_id: counterparty_node_id, @@ -5678,7 +5694,7 @@ where err: format!("Channel with id {} not fully established", next_hop_channel_id) }) } - funded_chan.context.get_short_channel_id().unwrap_or(funded_chan.context.outbound_scid_alias()) + funded_chan.funding.get_short_channel_id().unwrap_or(funded_chan.context.outbound_scid_alias()) } else { return Err(APIError::ChannelUnavailable { err: format!("Channel with id {} for the passed counterparty node_id {} is still opening.", @@ -6124,7 +6140,7 @@ where }; let logger = WithChannelContext::from(&self.logger, &optimal_channel.context, Some(payment_hash)); - let channel_description = if optimal_channel.context.get_short_channel_id() == Some(short_chan_id) { + let channel_description = if optimal_channel.funding.get_short_channel_id() == Some(short_chan_id) { "specified" } else { "alternate" @@ -6753,8 +6769,8 @@ where "Force-closing pending channel with ID {} for not establishing in a timely manner", context.channel_id()); let mut close_res = chan.force_shutdown(false, ClosureReason::HolderForceClosed { broadcasted_latest_txn: Some(false) }); - let context = chan.context_mut(); - locked_close_channel!(self, peer_state, context, close_res); + let (funding, context) = chan.funding_and_context_mut(); + locked_close_channel!(self, peer_state, context, funding, close_res); shutdown_channels.push(close_res); pending_msg_events.push(MessageSendEvent::HandleError { node_id: context.get_counterparty_node_id(), @@ -7680,7 +7696,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ ); let counterparty_node_id = channel.context.get_counterparty_node_id(); - let short_channel_id = channel.context.get_short_channel_id().unwrap_or(channel.context.outbound_scid_alias()); + let short_channel_id = channel.funding.get_short_channel_id().unwrap_or(channel.context.outbound_scid_alias()); let mut htlc_forwards = None; if !pending_forwards.is_empty() { @@ -7761,7 +7777,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ { let mut pending_events = self.pending_events.lock().unwrap(); emit_channel_pending_event!(pending_events, channel); - emit_channel_ready_event!(pending_events, channel); + emit_initial_channel_ready_event!(pending_events, channel); } (htlc_forwards, decode_update_add_htlcs) @@ -7964,7 +7980,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ if accept_0conf { // This should have been correctly configured by the call to Inbound(V1/V2)Channel::new. - debug_assert!(channel.context().minimum_depth().unwrap() == 0); + debug_assert!(channel.minimum_depth().unwrap() == 0); } else if channel.funding().get_channel_type().requires_zero_conf() { let send_msg_err_event = MessageSendEvent::HandleError { node_id: channel.context().get_counterparty_node_id(), @@ -8040,8 +8056,8 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ Some(funded_chan) => { // This covers non-zero-conf inbound `Channel`s that we are currently monitoring, but those // which have not yet had any confirmations on-chain. - if !funded_chan.funding.is_outbound() && funded_chan.context.minimum_depth().unwrap_or(1) != 0 && - funded_chan.context.get_funding_tx_confirmations(best_block_height) == 0 + if !funded_chan.funding.is_outbound() && chan.minimum_depth().unwrap_or(1) != 0 && + funded_chan.funding.get_funding_tx_confirmations(best_block_height) == 0 { num_unfunded_channels += 1; } @@ -8053,7 +8069,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ } // 0conf channels are not considered unfunded. - if chan.context().minimum_depth().unwrap_or(1) == 0 { + if chan.minimum_depth().unwrap_or(1) == 0 { continue; } @@ -8714,7 +8730,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ { let mut pending_events = self.pending_events.lock().unwrap(); - emit_channel_ready_event!(pending_events, chan); + emit_initial_channel_ready_event!(pending_events, chan); } Ok(()) @@ -9620,6 +9636,60 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ Err(MsgHandleErrInternal::send_err_msg_no_close("TODO(splicing): Splicing is not implemented (splice_ack)".to_owned(), msg.channel_id)) } + #[cfg(splicing)] + fn internal_splice_locked(&self, counterparty_node_id: &PublicKey, msg: &msgs::SpliceLocked) -> Result<(), MsgHandleErrInternal> { + let per_peer_state = self.per_peer_state.read().unwrap(); + let peer_state_mutex = per_peer_state.get(counterparty_node_id) + .ok_or_else(|| { + debug_assert!(false); + MsgHandleErrInternal::send_err_msg_no_close(format!("Can't find a peer matching the passed counterparty node_id {}", counterparty_node_id), msg.channel_id) + })?; + let mut peer_state_lock = peer_state_mutex.lock().unwrap(); + let peer_state = &mut *peer_state_lock; + + // Look for the channel + match peer_state.channel_by_id.entry(msg.channel_id) { + hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close(format!( + "Got a message for a channel from the wrong node! No such channel for the passed counterparty_node_id {}", + counterparty_node_id + ), msg.channel_id)), + hash_map::Entry::Occupied(mut chan_entry) => { + if let Some(chan) = chan_entry.get_mut().as_funded_mut() { + let logger = WithChannelContext::from(&self.logger, &chan.context, None); + let announcement_sigs_opt = try_channel_entry!( + self, peer_state, chan.splice_locked( + msg, &self.node_signer, self.chain_hash, &self.default_configuration, + &self.best_block.read().unwrap(), &&logger, + ), chan_entry + ); + if let Some(announcement_sigs) = announcement_sigs_opt { + let mut short_to_chan_info = self.short_to_chan_info.write().unwrap(); + insert_short_channel_id!(short_to_chan_info, chan); + + let mut pending_events = self.pending_events.lock().unwrap(); + pending_events.push_back((events::Event::ChannelReady { + channel_id: chan.context.channel_id(), + user_channel_id: chan.context.get_user_id(), + counterparty_node_id: chan.context.get_counterparty_node_id(), + funding_txo: chan.funding.get_funding_txo().map(|outpoint| outpoint.into_bitcoin_outpoint()), + channel_type: chan.funding.get_channel_type().clone(), + }, None)); + + log_trace!(logger, "Sending announcement_signatures for channel {}", chan.context.channel_id()); + peer_state.pending_msg_events.push(MessageSendEvent::SendAnnouncementSignatures { + node_id: counterparty_node_id.clone(), + msg: announcement_sigs, + }); + } + } else { + return Err(MsgHandleErrInternal::send_err_msg_no_close("Channel is not funded, cannot splice".to_owned(), msg.channel_id)); + } + }, + }; + + Ok(()) + } + /// Process pending events from the [`chain::Watch`], returning whether any events were processed. fn process_pending_monitor_events(&self) -> bool { debug_assert!(self.total_consistency_lock.try_write().is_err()); // Caller holds read lock @@ -9867,7 +9937,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ let context = &chan.context(); let logger = WithChannelContext::from(&self.logger, context, None); log_trace!(logger, "Removing channel {} now that the signer is unblocked", context.channel_id()); - locked_close_channel!(self, peer_state, context, shutdown_result); + locked_close_channel!(self, peer_state, context, chan.funding(), shutdown_result); shutdown_results.push(shutdown_result); false } else { @@ -9909,7 +9979,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ } debug_assert_eq!(shutdown_result_opt.is_some(), funded_chan.is_shutdown()); if let Some(mut shutdown_result) = shutdown_result_opt { - locked_close_channel!(self, peer_state, &funded_chan.context, shutdown_result); + locked_close_channel!(self, peer_state, &funded_chan.context, &funded_chan.funding, shutdown_result); shutdown_results.push(shutdown_result); } if let Some(tx) = tx_opt { @@ -10931,7 +11001,7 @@ where .iter() .filter(|(_, channel)| channel.context().is_usable()) .min_by_key(|(_, channel)| channel.context().channel_creation_height) - .and_then(|(_, channel)| channel.context().get_short_channel_id()), + .and_then(|(_, channel)| channel.funding().get_short_channel_id()), }) .collect::>(); @@ -11246,8 +11316,8 @@ where } // Clean up for removal. let mut close_res = chan.force_shutdown(false, ClosureReason::DisconnectedPeer); - let context = chan.context_mut(); - locked_close_channel!(self, peer_state, &context, close_res); + let (funding, context) = chan.funding_and_context_mut(); + locked_close_channel!(self, peer_state, &context, funding, close_res); failed_channels.push(close_res); false }); @@ -11634,6 +11704,29 @@ where channel.check_for_stale_feerate(&logger, feerate)?; } } + + // Remove any scids used by older funding transactions + if let Some(current_scid) = channel.funding.get_short_channel_id() { + let historical_scids = &mut channel.context.historical_scids; + if !historical_scids.is_empty() { + let mut short_to_chan_info = self.short_to_chan_info.write().unwrap(); + + // Remove an older SCID if the next funding has enough confirmations + for (scid, next_scid) in historical_scids + .iter() + .zip(historical_scids.iter().skip(1).chain(core::iter::once(¤t_scid))) + { + let funding_height = block_from_scid(*next_scid); + let retain_scid = funding_height + CHANNEL_ANNOUNCEMENT_PROPAGATION_DELAY > height; + if !retain_scid { + short_to_chan_info.remove(scid); + } + } + + historical_scids.retain(|scid| short_to_chan_info.contains_key(scid)); + } + } + channel.best_block_updated(height, header.time, self.chain_hash, &self.node_signer, &self.default_configuration, &&WithChannelContext::from(&self.logger, &channel.context, None)) }); @@ -11666,11 +11759,8 @@ where let mut peer_state_lock = peer_state_mutex.lock().unwrap(); let peer_state = &mut *peer_state_lock; for chan in peer_state.channel_by_id.values().filter_map(Channel::as_funded) { - let txid_opt = chan.funding.get_funding_txo(); - let height_opt = chan.context.get_funding_tx_confirmation_height(); - let hash_opt = chan.context.get_funding_tx_confirmed_in(); - if let (Some(funding_txo), Some(conf_height), Some(block_hash)) = (txid_opt, height_opt, hash_opt) { - res.push((funding_txo.txid, conf_height, Some(block_hash))); + for (funding_txid, conf_height, block_hash) in chan.get_relevant_txids() { + res.push((funding_txid, conf_height, block_hash)); } } } @@ -11682,15 +11772,18 @@ where PersistenceNotifierGuard::optionally_notify_skipping_background_events( self, || -> NotifyOption { NotifyOption::DoPersist }); self.do_chain_event(None, |channel| { - if let Some(funding_txo) = channel.funding.get_funding_txo() { - if funding_txo.txid == *txid { - channel.funding_transaction_unconfirmed(&&WithChannelContext::from(&self.logger, &channel.context, None)).map(|()| (None, Vec::new(), None)) - } else { Ok((None, Vec::new(), None)) } - } else { Ok((None, Vec::new(), None)) } + let logger = WithChannelContext::from(&self.logger, &channel.context, None); + channel.transaction_unconfirmed(txid, &&logger).map(|()| (None, Vec::new(), None)) }); } } +pub(super) enum FundingConfirmedMessage { + Establishment(msgs::ChannelReady), + #[cfg(splicing)] + Splice(msgs::SpliceLocked), +} + impl ChannelManager where M::Target: chain::Watch<::EcdsaSigner>, @@ -11706,7 +11799,7 @@ where /// Calls a function which handles an on-chain event (blocks dis/connected, transactions /// un/confirmed, etc) on each channel, handling any resulting errors or messages generated by /// the function. - fn do_chain_event) -> Result<(Option, Vec<(HTLCSource, PaymentHash)>, Option), ClosureReason>> + fn do_chain_event) -> Result<(Option, Vec<(HTLCSource, PaymentHash)>, Option), ClosureReason>> (&self, height_opt: Option, f: FN) { // Note that we MUST NOT end up calling methods on self.chain_monitor here - we're called // during initialization prior to the chain_monitor being fully configured in some cases. @@ -11727,7 +11820,7 @@ where None => true, Some(funded_channel) => { let res = f(funded_channel); - if let Ok((channel_ready_opt, mut timed_out_pending_htlcs, announcement_sigs)) = res { + if let Ok((funding_confirmed_opt, mut timed_out_pending_htlcs, announcement_sigs)) = res { for (source, payment_hash) in timed_out_pending_htlcs.drain(..) { let reason = LocalHTLCFailureReason::CLTVExpiryTooSoon; let data = self.get_htlc_inbound_temp_fail_data(reason); @@ -11735,31 +11828,55 @@ where HTLCHandlingFailureType::Forward { node_id: Some(funded_channel.context.get_counterparty_node_id()), channel_id: funded_channel.context.channel_id() })); } let logger = WithChannelContext::from(&self.logger, &funded_channel.context, None); - if let Some(channel_ready) = channel_ready_opt { - send_channel_ready!(self, pending_msg_events, funded_channel, channel_ready); - if funded_channel.context.is_usable() { - log_trace!(logger, "Sending channel_ready with private initial channel_update for our counterparty on channel {}", funded_channel.context.channel_id()); - if let Ok(msg) = self.get_channel_update_for_unicast(funded_channel) { - pending_msg_events.push(MessageSendEvent::SendChannelUpdate { - node_id: funded_channel.context.get_counterparty_node_id(), - msg, - }); + match funding_confirmed_opt { + Some(FundingConfirmedMessage::Establishment(channel_ready)) => { + send_channel_ready!(self, pending_msg_events, funded_channel, channel_ready); + if funded_channel.context.is_usable() { + log_trace!(logger, "Sending channel_ready with private initial channel_update for our counterparty on channel {}", funded_channel.context.channel_id()); + if let Ok(msg) = self.get_channel_update_for_unicast(funded_channel) { + pending_msg_events.push(MessageSendEvent::SendChannelUpdate { + node_id: funded_channel.context.get_counterparty_node_id(), + msg, + }); + } + } else { + log_trace!(logger, "Sending channel_ready WITHOUT channel_update for {}", funded_channel.context.channel_id()); } - } else { - log_trace!(logger, "Sending channel_ready WITHOUT channel_update for {}", funded_channel.context.channel_id()); - } + }, + #[cfg(splicing)] + Some(FundingConfirmedMessage::Splice(splice_locked)) => { + if announcement_sigs.is_some() { + let mut short_to_chan_info = self.short_to_chan_info.write().unwrap(); + insert_short_channel_id!(short_to_chan_info, funded_channel); + + let mut pending_events = self.pending_events.lock().unwrap(); + pending_events.push_back((events::Event::ChannelReady { + channel_id: funded_channel.context.channel_id(), + user_channel_id: funded_channel.context.get_user_id(), + counterparty_node_id: funded_channel.context.get_counterparty_node_id(), + funding_txo: funded_channel.funding.get_funding_txo().map(|outpoint| outpoint.into_bitcoin_outpoint()), + channel_type: funded_channel.funding.get_channel_type().clone(), + }, None)); + } + + pending_msg_events.push(MessageSendEvent::SendSpliceLocked { + node_id: funded_channel.context.get_counterparty_node_id(), + msg: splice_locked, + }); + }, + None => {}, } { let mut pending_events = self.pending_events.lock().unwrap(); - emit_channel_ready_event!(pending_events, funded_channel); + emit_initial_channel_ready_event!(pending_events, funded_channel); } if let Some(height) = height_opt { // (re-)broadcast signed `channel_announcement`s and // `channel_update`s for any channels less than a week old. let funding_conf_height = - funded_channel.context.get_funding_tx_confirmation_height().unwrap_or(height); + funded_channel.funding.get_funding_tx_confirmation_height().unwrap_or(height); // To avoid broadcast storms after each block, only // re-broadcast every hour (6 blocks) after the initial // broadcast, or if this is the first time we're ready to @@ -11798,7 +11915,7 @@ where }); } if funded_channel.is_our_channel_ready() { - if let Some(real_scid) = funded_channel.context.get_short_channel_id() { + if let Some(real_scid) = funded_channel.funding.get_short_channel_id() { // If we sent a 0conf channel_ready, and now have an SCID, we add it // to the short_to_chan_info map here. Note that we check whether we // can relay using the real SCID at relay-time (i.e. @@ -11817,7 +11934,7 @@ where // reorged out of the main chain. Close the channel. let reason_message = format!("{}", reason); let mut close_res = funded_channel.context.force_shutdown(&funded_channel.funding, true, reason); - locked_close_channel!(self, peer_state, &funded_channel.context, close_res); + locked_close_channel!(self, peer_state, &funded_channel.context, &funded_channel.funding, close_res); failed_channels.push(close_res); if let Ok(update) = self.get_channel_update_for_broadcast(&funded_channel) { let mut pending_broadcast_messages = self.pending_broadcast_messages.lock().unwrap(); @@ -12122,9 +12239,16 @@ where #[cfg(splicing)] fn handle_splice_locked(&self, counterparty_node_id: PublicKey, msg: &msgs::SpliceLocked) { - let _: Result<(), _> = handle_error!(self, Err(MsgHandleErrInternal::send_err_msg_no_close( - "Splicing not supported (splice_locked)".to_owned(), - msg.channel_id)), counterparty_node_id); + let _persistence_guard = PersistenceNotifierGuard::optionally_notify(self, || { + let res = self.internal_splice_locked(&counterparty_node_id, msg); + let persist = match &res { + Err(e) if e.closes_channel() => NotifyOption::DoPersist, + Err(_) => NotifyOption::SkipPersistHandleEvents, + Ok(()) => NotifyOption::DoPersist, + }; + let _ = handle_error!(self, res, counterparty_node_id); + persist + }); } fn handle_shutdown(&self, counterparty_node_id: PublicKey, msg: &msgs::Shutdown) { @@ -13899,9 +14023,14 @@ where log_info!(logger, "Successfully loaded channel {} at update_id {} against monitor at update id {} with {} blocked updates", &channel.context.channel_id(), channel.context.get_latest_monitor_update_id(), monitor.get_latest_update_id(), channel.blocked_monitor_updates_pending()); - if let Some(short_channel_id) = channel.context.get_short_channel_id() { + if let Some(short_channel_id) = channel.funding.get_short_channel_id() { short_to_chan_info.insert(short_channel_id, (channel.context.get_counterparty_node_id(), channel.context.channel_id())); } + + for short_channel_id in &channel.context.historical_scids { + short_to_chan_info.insert(*short_channel_id, (channel.context.get_counterparty_node_id(), channel.context.channel_id())); + } + per_peer_state.entry(channel.context.get_counterparty_node_id()) .or_insert_with(|| Mutex::new(empty_peer_state())) .get_mut().unwrap() diff --git a/lightning/src/ln/payment_tests.rs b/lightning/src/ln/payment_tests.rs index 389d0f15fb9..552acef4616 100644 --- a/lightning/src/ln/payment_tests.rs +++ b/lightning/src/ln/payment_tests.rs @@ -1927,7 +1927,7 @@ fn test_trivial_inflight_htlc_tracking() { let chan_1_used_liquidity = inflight_htlcs.used_liquidity_msat( &NodeId::from_pubkey(&node_a_id), &NodeId::from_pubkey(&node_b_id), - channel_1.context().get_short_channel_id().unwrap(), + channel_1.funding().get_short_channel_id().unwrap(), ); assert_eq!(chan_1_used_liquidity, None); } @@ -1940,7 +1940,7 @@ fn test_trivial_inflight_htlc_tracking() { let chan_2_used_liquidity = inflight_htlcs.used_liquidity_msat( &NodeId::from_pubkey(&node_b_id), &NodeId::from_pubkey(&node_c_id), - channel_2.context().get_short_channel_id().unwrap(), + channel_2.funding().get_short_channel_id().unwrap(), ); assert_eq!(chan_2_used_liquidity, None); @@ -1968,7 +1968,7 @@ fn test_trivial_inflight_htlc_tracking() { let chan_1_used_liquidity = inflight_htlcs.used_liquidity_msat( &NodeId::from_pubkey(&node_a_id), &NodeId::from_pubkey(&node_b_id), - channel_1.context().get_short_channel_id().unwrap(), + channel_1.funding().get_short_channel_id().unwrap(), ); // First hop accounts for expected 1000 msat fee assert_eq!(chan_1_used_liquidity, Some(501000)); @@ -1982,7 +1982,7 @@ fn test_trivial_inflight_htlc_tracking() { let chan_2_used_liquidity = inflight_htlcs.used_liquidity_msat( &NodeId::from_pubkey(&node_b_id), &NodeId::from_pubkey(&node_c_id), - channel_2.context().get_short_channel_id().unwrap(), + channel_2.funding().get_short_channel_id().unwrap(), ); assert_eq!(chan_2_used_liquidity, Some(500000)); @@ -2010,7 +2010,7 @@ fn test_trivial_inflight_htlc_tracking() { let chan_1_used_liquidity = inflight_htlcs.used_liquidity_msat( &NodeId::from_pubkey(&node_a_id), &NodeId::from_pubkey(&node_b_id), - channel_1.context().get_short_channel_id().unwrap(), + channel_1.funding().get_short_channel_id().unwrap(), ); assert_eq!(chan_1_used_liquidity, None); } @@ -2023,7 +2023,7 @@ fn test_trivial_inflight_htlc_tracking() { let chan_2_used_liquidity = inflight_htlcs.used_liquidity_msat( &NodeId::from_pubkey(&node_b_id), &NodeId::from_pubkey(&node_c_id), - channel_2.context().get_short_channel_id().unwrap(), + channel_2.funding().get_short_channel_id().unwrap(), ); assert_eq!(chan_2_used_liquidity, None); } @@ -2073,7 +2073,7 @@ fn test_holding_cell_inflight_htlcs() { let used_liquidity = inflight_htlcs.used_liquidity_msat( &NodeId::from_pubkey(&node_a_id), &NodeId::from_pubkey(&node_b_id), - channel.context().get_short_channel_id().unwrap(), + channel.funding().get_short_channel_id().unwrap(), ); assert_eq!(used_liquidity, Some(2000000));