diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index 7943ed98719..fee74aada0d 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -80,7 +80,7 @@ use crate::util::config::{ MaxDustHTLCExposure, UserConfig, }; use crate::util::errors::APIError; -use crate::util::logger::{Logger, Record, WithContext}; +use crate::util::logger::{Level as LoggerLevel, Logger, Record, WithContext}; use crate::util::scid_utils::{block_from_scid, scid_from_parts}; use crate::util::ser::{Readable, ReadableArgs, RequiredWrapper, Writeable, Writer}; use crate::util::wallet_utils::Input; @@ -679,10 +679,9 @@ mod state_flags { pub const LOCAL_SHUTDOWN_SENT: u32 = 1 << 11; pub const SHUTDOWN_COMPLETE: u32 = 1 << 12; pub const WAITING_FOR_BATCH: u32 = 1 << 13; - pub const AWAITING_QUIESCENCE: u32 = 1 << 14; - pub const LOCAL_STFU_SENT: u32 = 1 << 15; - pub const REMOTE_STFU_SENT: u32 = 1 << 16; - pub const QUIESCENT: u32 = 1 << 17; + pub const LOCAL_STFU_SENT: u32 = 1 << 14; + pub const REMOTE_STFU_SENT: u32 = 1 << 15; + pub const QUIESCENT: u32 = 1 << 16; } define_state_flags!( @@ -749,13 +748,8 @@ define_state_flags!( implicit ACK, so instead we have to hold them away temporarily to be sent later.", AWAITING_REMOTE_REVOKE, state_flags::AWAITING_REMOTE_REVOKE, is_awaiting_remote_revoke, set_awaiting_remote_revoke, clear_awaiting_remote_revoke), - ("Indicates a local request has been made for the channel to become quiescent. Both nodes \ - must send `stfu` for the channel to become quiescent. This flag will be cleared and we \ - will no longer attempt quiescence if either node requests a shutdown.", - AWAITING_QUIESCENCE, state_flags::AWAITING_QUIESCENCE, - is_awaiting_quiescence, set_awaiting_quiescence, clear_awaiting_quiescence), ("Indicates we have sent a `stfu` message to the counterparty. This message can only be sent \ - if either `AWAITING_QUIESCENCE` or `REMOTE_STFU_SENT` is set. Shutdown requests are \ + if `REMOTE_STFU_SENT` is set, or a `QuiescentAction` is pending. Shutdown requests are \ rejected if this flag is set.", LOCAL_STFU_SENT, state_flags::LOCAL_STFU_SENT, is_local_stfu_sent, set_local_stfu_sent, clear_local_stfu_sent), @@ -950,12 +944,6 @@ impl ChannelState { clear_awaiting_remote_revoke, ChannelReady ); - impl_state_flag!( - is_awaiting_quiescence, - set_awaiting_quiescence, - clear_awaiting_quiescence, - ChannelReady - ); impl_state_flag!(is_local_stfu_sent, set_local_stfu_sent, clear_local_stfu_sent, ChannelReady); impl_state_flag!( is_remote_stfu_sent, @@ -1750,10 +1738,6 @@ where let splice_funding_failed = if let ChannelPhase::Funded(chan) = &mut self.phase { // Reset any quiescence-related state as it is implicitly terminated once disconnected. if matches!(chan.context.channel_state, ChannelState::ChannelReady(_)) { - if chan.quiescent_action.is_some() { - // If we were trying to get quiescent, try again after reconnection. - chan.context.channel_state.set_awaiting_quiescence(); - } chan.context.channel_state.clear_local_stfu_sent(); chan.context.channel_state.clear_remote_stfu_sent(); if chan.should_reset_pending_splice_state(false) { @@ -7081,39 +7065,41 @@ where shutdown_result } + fn abandon_quiescent_action(&mut self) -> Option { + match self.quiescent_action.take() { + Some(QuiescentAction::LegacySplice(instructions)) => { + let (inputs, outputs) = instructions.into_contributed_inputs_and_outputs(); + Some(SpliceFundingFailed { + funding_txo: None, + channel_type: None, + contributed_inputs: inputs, + contributed_outputs: outputs, + }) + }, + Some(QuiescentAction::Splice { contribution, .. }) => { + let (inputs, outputs) = contribution.into_contributed_inputs_and_outputs(); + Some(SpliceFundingFailed { + funding_txo: None, + channel_type: None, + contributed_inputs: inputs, + contributed_outputs: outputs, + }) + }, + #[cfg(any(test, fuzzing, feature = "_test_utils"))] + Some(quiescent_action) => { + self.quiescent_action = Some(quiescent_action); + None + }, + None => None, + } + } + fn maybe_fail_splice_negotiation(&mut self) -> Option { if matches!(self.context.channel_state, ChannelState::ChannelReady(_)) { if self.should_reset_pending_splice_state(false) { self.reset_pending_splice_state() } else { - match self.quiescent_action.take() { - Some(QuiescentAction::LegacySplice(instructions)) => { - self.context.channel_state.clear_awaiting_quiescence(); - let (inputs, outputs) = instructions.into_contributed_inputs_and_outputs(); - Some(SpliceFundingFailed { - funding_txo: None, - channel_type: None, - contributed_inputs: inputs, - contributed_outputs: outputs, - }) - }, - Some(QuiescentAction::Splice { contribution, .. }) => { - self.context.channel_state.clear_awaiting_quiescence(); - let (inputs, outputs) = contribution.into_contributed_inputs_and_outputs(); - Some(SpliceFundingFailed { - funding_txo: None, - channel_type: None, - contributed_inputs: inputs, - contributed_outputs: outputs, - }) - }, - #[cfg(any(test, fuzzing, feature = "_test_utils"))] - Some(quiescent_action) => { - self.quiescent_action = Some(quiescent_action); - None - }, - None => None, - } + self.abandon_quiescent_action() } } else { None @@ -10656,7 +10642,12 @@ where &mut self, logger: &L, signer_provider: &SP, their_features: &InitFeatures, msg: &msgs::Shutdown, ) -> Result< - (Option, Option, Vec<(HTLCSource, PaymentHash)>), + ( + Option, + Option, + Vec<(HTLCSource, PaymentHash)>, + Option, + ), ChannelError, > { if self.context.channel_state.is_peer_disconnected() { @@ -10747,11 +10738,6 @@ where // From here on out, we may not fail! self.context.channel_state.set_remote_shutdown_sent(); - if self.context.channel_state.is_awaiting_quiescence() { - // We haven't been able to send `stfu` yet, and there's no point in attempting - // quiescence anymore since the counterparty wishes to close the channel. - self.context.channel_state.clear_awaiting_quiescence(); - } self.context.update_time_counter += 1; let monitor_update = if update_shutdown_script { @@ -10802,7 +10788,9 @@ where self.context.channel_state.set_local_shutdown_sent(); self.context.update_time_counter += 1; - Ok((shutdown, monitor_update, dropped_outbound_htlcs)) + let splice_funding_failed = self.abandon_quiescent_action(); + + Ok((shutdown, monitor_update, dropped_outbound_htlcs, splice_funding_failed)) } fn build_signed_closing_transaction( @@ -11526,17 +11514,6 @@ where let announcement_sigs = self.get_announcement_sigs(node_signer, chain_hash, user_config, block_height, logger); - if let Some(quiescent_action) = self.quiescent_action.as_ref() { - // TODO(splicing): If we didn't win quiescence, then we can contribute as an acceptor - // instead of waiting for the splice to lock. - if matches!( - quiescent_action, - QuiescentAction::Splice { .. } | QuiescentAction::LegacySplice(_) - ) { - self.context.channel_state.set_awaiting_quiescence(); - } - } - Some(SpliceFundingPromotion { funding_txo, monitor_update, @@ -13240,7 +13217,12 @@ where target_feerate_sats_per_kw: Option, override_shutdown_script: Option, logger: &L, ) -> Result< - (msgs::Shutdown, Option, Vec<(HTLCSource, PaymentHash)>), + ( + msgs::Shutdown, + Option, + Vec<(HTLCSource, PaymentHash)>, + Option, + ), APIError, > { let logger = WithChannelContext::from(logger, &self.context, None); @@ -13314,9 +13296,6 @@ where // From here on out, we may not fail! self.context.target_closing_feerate_sats_per_kw = target_feerate_sats_per_kw; self.context.channel_state.set_local_shutdown_sent(); - if self.context.channel_state.is_awaiting_quiescence() { - self.context.channel_state.clear_awaiting_quiescence(); - } self.context.local_initiated_shutdown = Some(()); self.context.update_time_counter += 1; @@ -13365,7 +13344,9 @@ where "we can't both complete shutdown and return a monitor update" ); - Ok((shutdown, monitor_update, dropped_outbound_htlcs)) + let splice_funding_failed = self.abandon_quiescent_action(); + + Ok((shutdown, monitor_update, dropped_outbound_htlcs, splice_funding_failed)) } // Miscellaneous utilities @@ -13410,65 +13391,17 @@ where ); return Err(action); } + // Since we don't have a pending quiescent action, we should never be in a state where we + // sent `stfu` without already having become quiescent. + debug_assert!(!self.context.channel_state.is_local_stfu_sent()); self.quiescent_action = Some(action); - if self.context.channel_state.is_quiescent() - || self.context.channel_state.is_awaiting_quiescence() - || self.context.channel_state.is_local_stfu_sent() - { - log_debug!(logger, "Channel is either pending quiescence or already quiescent"); + if self.context.channel_state.is_quiescent() { + log_debug!(logger, "Channel is already quiescent"); return Ok(None); } - self.context.channel_state.set_awaiting_quiescence(); - if self.context.is_live() { - match self.send_stfu(logger) { - Ok(stfu) => Ok(Some(stfu)), - Err(e) => { - log_debug!(logger, "{e}"); - Ok(None) - }, - } - } else { - log_debug!(logger, "Waiting for peer reconnection to send stfu"); - Ok(None) - } - } - - // Assumes we are either awaiting quiescence or our counterparty has requested quiescence. - #[rustfmt::skip] - pub fn send_stfu(&mut self, logger: &L) -> Result { - debug_assert!(!self.context.channel_state.is_local_stfu_sent()); - debug_assert!( - self.context.channel_state.is_awaiting_quiescence() - || self.context.channel_state.is_remote_stfu_sent() - ); - debug_assert!(self.context.is_live()); - - if self.context.is_waiting_on_peer_pending_channel_update() - || self.context.is_monitor_or_signer_pending_channel_update() - { - return Err("We cannot send `stfu` while state machine is pending") - } - - let initiator = if self.context.channel_state.is_remote_stfu_sent() { - // We may have also attempted to initiate quiescence. - self.context.channel_state.clear_awaiting_quiescence(); - self.context.channel_state.clear_remote_stfu_sent(); - self.context.channel_state.set_quiescent(); - // We are sending an stfu in response to our couterparty's stfu, but had not yet sent - // our own stfu (even if `awaiting_quiescence` was set). Thus, the counterparty is the - // initiator and they can do "something fundamental". - false - } else { - log_debug!(logger, "Sending stfu as quiescence initiator"); - debug_assert!(self.context.channel_state.is_awaiting_quiescence()); - self.context.channel_state.clear_awaiting_quiescence(); - self.context.channel_state.set_local_stfu_sent(); - true - }; - - Ok(msgs::Stfu { channel_id: self.context.channel_id, initiator }) + Ok(self.try_send_stfu(false, logger)) } #[rustfmt::skip] @@ -13505,10 +13438,7 @@ where self.context.channel_state.set_remote_stfu_sent(); log_debug!(logger, "Received counterparty stfu proposing quiescence"); - return self - .send_stfu(logger) - .map(|stfu| Some(StfuResponse::Stfu(stfu))) - .map_err(|e| ChannelError::Ignore(e.to_owned())); + return Ok(self.try_send_stfu(false, logger).map(|stfu| StfuResponse::Stfu(stfu))) } // We already sent `stfu` and are now processing theirs. It may be in response to ours, or @@ -13610,17 +13540,28 @@ where Ok(None) } - pub fn try_send_stfu( - &mut self, logger: &L, - ) -> Result, ChannelError> { + pub fn try_send_stfu(&mut self, is_retry: bool, logger: &L) -> Option { // We must never see both stfu flags set, we always set the quiescent flag instead. debug_assert!( !(self.context.channel_state.is_local_stfu_sent() && self.context.channel_state.is_remote_stfu_sent()) ); + // We only need to send `stfu` when we're awaiting quiescence and haven't sent it yet, or + // in response to a counterparty one. + if self.quiescent_action.is_none() && !self.context.channel_state.is_remote_stfu_sent() { + return None; + } + if self.context.channel_state.is_local_stfu_sent() + || self.context.channel_state.is_quiescent() + { + return None; + } + + let logger_level = if is_retry { LoggerLevel::Trace } else { LoggerLevel::Debug }; if !self.context.is_live() { - return Ok(None); + log_given_level!(logger, logger_level, "Waiting for peer reconnection to send stfu"); + return None; } if let Some(action) = self.quiescent_action.as_ref() { @@ -13630,27 +13571,39 @@ where let has_splice_action = matches!(action, QuiescentAction::Splice { .. }) || matches!(action, QuiescentAction::LegacySplice(_)); if has_splice_action && self.pending_splice.is_some() { - return Ok(None); + log_given_level!( + logger, + logger_level, + "Waiting for pending splice to lock before sending stfu for new splice" + ); + return None; } } - // We need to send our `stfu`, either because we're trying to initiate quiescence, or the - // counterparty is and we've yet to send ours. - if self.context.channel_state.is_awaiting_quiescence() - || (self.context.channel_state.is_remote_stfu_sent() - && !self.context.channel_state.is_local_stfu_sent()) + if self.context.is_waiting_on_peer_pending_channel_update() + || self.context.is_monitor_or_signer_pending_channel_update() { - return self - .send_stfu(logger) - .map(|stfu| Some(stfu)) - .map_err(|e| ChannelError::Ignore(e.to_owned())); + log_given_level!( + logger, + logger_level, + "Waiting for state machine pending changes to complete before sending stfu" + ); + return None; } - // We're either: - // - already quiescent - // - in a state where quiescence is not possible - // - not currently trying to become quiescent - Ok(None) + let initiator = if self.context.channel_state.is_remote_stfu_sent() { + // Since we may have also attempted to initiate quiescence but the counterparty + // initiated first, we'll retry after we're no longer quiescent. + self.context.channel_state.clear_remote_stfu_sent(); + self.context.channel_state.set_quiescent(); + false + } else { + log_debug!(logger, "Sending stfu as quiescence initiator"); + self.context.channel_state.set_local_stfu_sent(); + true + }; + + Some(msgs::Stfu { channel_id: self.context.channel_id, initiator }) } #[cfg(any(test, fuzzing, feature = "_test_utils"))] @@ -13658,7 +13611,6 @@ where pub fn exit_quiescence(&mut self) -> bool { // Make sure we either finished the quiescence handshake and are quiescent, or we never // attempted to initiate quiescence at all. - debug_assert!(!self.context.channel_state.is_awaiting_quiescence()); debug_assert!(!self.context.channel_state.is_local_stfu_sent()); debug_assert!(!self.context.channel_state.is_remote_stfu_sent()); @@ -14763,11 +14715,6 @@ impl Writeable for FundedChannel { match channel_state { ChannelState::AwaitingChannelReady(_) => {}, ChannelState::ChannelReady(_) => { - if self.quiescent_action.is_some() { - // If we're trying to get quiescent to do something, try again when we - // reconnect to the peer. - channel_state.set_awaiting_quiescence(); - } channel_state.clear_local_stfu_sent(); channel_state.clear_remote_stfu_sent(); if self.should_reset_pending_splice_state(false) diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 08cbb6f6bf7..0e764d6cc9b 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -3901,15 +3901,31 @@ impl< if let Some(chan) = chan_entry.get_mut().as_funded_mut() { let funding_txo_opt = chan.funding.get_funding_txo(); let their_features = &peer_state.latest_features; - let (shutdown_msg, mut monitor_update_opt, htlcs) = chan.get_shutdown( - &self.signer_provider, - their_features, - target_feerate_sats_per_1000_weight, - override_shutdown_script, - &self.logger, - )?; + let (shutdown_msg, mut monitor_update_opt, htlcs, splice_funding_failed) = + chan.get_shutdown( + &self.signer_provider, + their_features, + target_feerate_sats_per_1000_weight, + override_shutdown_script, + &self.logger, + )?; failed_htlcs = htlcs; + if let Some(splice_funding_failed) = splice_funding_failed { + self.pending_events.lock().unwrap().push_back(( + events::Event::SpliceFailed { + channel_id: *chan_id, + counterparty_node_id: *counterparty_node_id, + user_channel_id: chan.context().get_user_id(), + abandoned_funding_txo: splice_funding_failed.funding_txo, + channel_type: splice_funding_failed.channel_type, + contributed_inputs: splice_funding_failed.contributed_inputs, + contributed_outputs: splice_funding_failed.contributed_outputs, + }, + None, + )); + } + // We can send the `shutdown` message before updating the `ChannelMonitor` // here as we don't need the monitor update to complete until we send a // `shutdown_signed`, which we'll delay if we're pending a monitor update. @@ -11779,19 +11795,31 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ } let funding_txo_opt = chan.funding.get_funding_txo(); - let (shutdown, monitor_update_opt, htlcs) = try_channel_entry!( - self, - peer_state, - chan.shutdown( - &self.logger, - &self.signer_provider, - &peer_state.latest_features, - &msg - ), - chan_entry + let res = chan.shutdown( + &self.logger, + &self.signer_provider, + &peer_state.latest_features, + &msg, ); + let (shutdown, monitor_update_opt, htlcs, splice_funding_failed) = + try_channel_entry!(self, peer_state, res, chan_entry); dropped_htlcs = htlcs; + if let Some(splice_funding_failed) = splice_funding_failed { + self.pending_events.lock().unwrap().push_back(( + events::Event::SpliceFailed { + channel_id: msg.channel_id, + counterparty_node_id: *counterparty_node_id, + user_channel_id: chan.context().get_user_id(), + abandoned_funding_txo: splice_funding_failed.funding_txo, + channel_type: splice_funding_failed.channel_type, + contributed_inputs: splice_funding_failed.contributed_inputs, + contributed_outputs: splice_funding_failed.contributed_outputs, + }, + None, + )); + } + if let Some(msg) = shutdown { // We can send the `shutdown` message before updating the `ChannelMonitor` // here as we don't need the monitor update to complete until we send a @@ -13342,17 +13370,11 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ let logger = WithContext::from( &self.logger, Some(*counterparty_node_id), Some(*channel_id), None ); - match funded_chan.try_send_stfu(&&logger) { - Ok(None) => {}, - Ok(Some(stfu)) => { - pending_msg_events.push(MessageSendEvent::SendStfu { - node_id: chan.context().get_counterparty_node_id(), - msg: stfu, - }); - }, - Err(e) => { - log_debug!(logger, "Could not advance quiescence handshake: {}", e); - } + if let Some(stfu) = funded_chan.try_send_stfu(true, &&logger) { + pending_msg_events.push(MessageSendEvent::SendStfu { + node_id: chan.context().get_counterparty_node_id(), + msg: stfu, + }); } } } diff --git a/lightning/src/ln/quiescence_tests.rs b/lightning/src/ln/quiescence_tests.rs index d972fb6a5c5..56dc4d42797 100644 --- a/lightning/src/ln/quiescence_tests.rs +++ b/lightning/src/ln/quiescence_tests.rs @@ -35,6 +35,11 @@ fn test_quiescence_tie() { assert!(nodes[0].node.exit_quiescence(&nodes[1].node.get_our_node_id(), &chan_id).unwrap()); assert!(nodes[1].node.exit_quiescence(&nodes[0].node.get_our_node_id(), &chan_id).unwrap()); + + // Since node 1 lost the tie, they'll attempt quiescence again. + let stfu = + get_event_msg!(nodes[1], MessageSendEvent::SendStfu, nodes[0].node.get_our_node_id()); + assert!(stfu.initiator); } #[test] diff --git a/lightning/src/ln/splicing_tests.rs b/lightning/src/ln/splicing_tests.rs index 92a298f6ef1..fc18a9ec766 100644 --- a/lightning/src/ln/splicing_tests.rs +++ b/lightning/src/ln/splicing_tests.rs @@ -1145,6 +1145,49 @@ fn fails_initiating_concurrent_splices(reconnect: bool) { ); } +#[test] +fn test_initiating_splice_holds_stfu_with_pending_splice() { + // Test that we don't send stfu too early for a new splice while we're already pending one. + let chanmon_cfgs = create_chanmon_cfgs(2); + let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); + let config = test_default_channel_config(); + let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, Some(config)]); + let nodes = create_network(2, &node_cfgs, &node_chanmgrs); + + let node_0_id = nodes[0].node.get_our_node_id(); + provide_utxo_reserves(&nodes, 2, Amount::ONE_BTC); + + let initial_channel_value_sat = 100_000; + let (_, _, channel_id, _) = + create_announced_chan_between_nodes_with_value(&nodes, 0, 1, initial_channel_value_sat, 0); + + // Have both nodes attempt a splice, but only node 0 will call back and negotiate the splice. + let value_added = Amount::from_sat(10_000); + let funding_contribution_0 = initiate_splice_in(&nodes[0], &nodes[1], channel_id, value_added); + + let feerate = FeeRate::from_sat_per_kwu(FEERATE_FLOOR_SATS_PER_KW as u64); + let funding_template = nodes[1].node.splice_channel(&channel_id, &node_0_id, feerate).unwrap(); + + let (splice_tx, _) = splice_channel(&nodes[0], &nodes[1], channel_id, funding_contribution_0); + + // With the splice negotiated, have node 1 call back. This will queue the quiescent action, but + // it shouldn't send stfu yet as there's a pending splice. + let wallet = WalletSync::new(Arc::clone(&nodes[1].wallet_source), &nodes[1].logger); + let funding_contribution = funding_template.splice_in_sync(value_added, &wallet).unwrap(); + nodes[1] + .node + .funding_contributed(&channel_id, &node_0_id, funding_contribution.clone(), None) + .unwrap(); + assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty()); + + mine_transaction(&nodes[0], &splice_tx); + mine_transaction(&nodes[1], &splice_tx); + let stfu = lock_splice_after_blocks(&nodes[0], &nodes[1], 5); + assert!( + matches!(stfu, Some(MessageSendEvent::SendStfu { node_id, .. }) if node_id == node_0_id) + ); +} + #[cfg(test)] #[derive(PartialEq)] enum SpliceStatus { @@ -2363,6 +2406,66 @@ fn fail_quiescent_action_on_channel_close() { check_added_monitors(&nodes[0], 1); } +#[test] +fn abandon_splice_quiescent_action_on_shutdown() { + do_abandon_splice_quiescent_action_on_shutdown(true); + do_abandon_splice_quiescent_action_on_shutdown(false); +} + +#[cfg(test)] +fn do_abandon_splice_quiescent_action_on_shutdown(local_shutdown: bool) { + let chanmon_cfgs = create_chanmon_cfgs(2); + let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); + let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]); + let nodes = create_network(2, &node_cfgs, &node_chanmgrs); + provide_utxo_reserves(&nodes, 1, Amount::ONE_BTC); + + let node_id_0 = nodes[0].node.get_our_node_id(); + let node_id_1 = nodes[1].node.get_our_node_id(); + + let initial_channel_capacity = 100_000; + let (_, _, channel_id, _) = + create_announced_chan_between_nodes_with_value(&nodes, 0, 1, initial_channel_capacity, 0); + + // Since we cannot close after having sent `stfu`, send an HTLC so that when we attempt to + // splice, the `stfu` message is held back. + let (route, payment_hash, _payment_preimage, payment_secret) = + get_route_and_payment_hash!(&nodes[0], &nodes[1], 1_000_000); + let onion = RecipientOnionFields::secret_only(payment_secret); + let payment_id = PaymentId(payment_hash.0); + nodes[0].node.send_payment_with_route(route, payment_hash, onion, payment_id).unwrap(); + let update = get_htlc_update_msgs(&nodes[0], &node_id_1); + check_added_monitors(&nodes[0], 1); + + nodes[1].node.handle_update_add_htlc(node_id_0, &update.update_add_htlcs[0]); + nodes[1].node.handle_commitment_signed(node_id_0, &update.commitment_signed[0]); + check_added_monitors(&nodes[1], 1); + let (revoke_and_ack, _) = get_revoke_commit_msgs(&nodes[1], &node_id_0); + + nodes[0].node.handle_revoke_and_ack(node_id_1, &revoke_and_ack); + check_added_monitors(&nodes[0], 1); + + // Attempt the splice. `stfu` should not go out yet as the state machine is pending. + let splice_in_amount = initial_channel_capacity / 2; + let _ = + initiate_splice_in(&nodes[0], &nodes[1], channel_id, Amount::from_sat(splice_in_amount)); + assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty()); + + // Close the channel. We should see a `SpliceFailed` event for the pending splice + // `QuiescentAction`. + let (closer_node, closee_node) = + if local_shutdown { (&nodes[0], &nodes[1]) } else { (&nodes[1], &nodes[0]) }; + let closer_node_id = closer_node.node.get_our_node_id(); + let closee_node_id = closee_node.node.get_our_node_id(); + + closer_node.node.close_channel(&channel_id, &closee_node_id).unwrap(); + let shutdown = get_event_msg!(closer_node, MessageSendEvent::SendShutdown, closee_node_id); + closee_node.node.handle_shutdown(closer_node_id, &shutdown); + + let _ = get_event!(nodes[0], Event::SpliceFailed); + let _ = get_event_msg!(closee_node, MessageSendEvent::SendShutdown, closer_node_id); +} + #[cfg(test)] fn do_test_splice_with_inflight_htlc_forward_and_resolution(expire_scid_pre_forward: bool) { // Test that we are still able to forward and resolve HTLCs while the original SCIDs contained