diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index edcaacfedc6..2341128c74d 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -1265,6 +1265,7 @@ pub(super) struct ReestablishResponses { pub shutdown_msg: Option, pub tx_signatures: Option, pub tx_abort: Option, + pub splice_locked: Option, pub inferred_splice_locked: Option, } @@ -3503,6 +3504,12 @@ pub(super) struct ChannelContext { /// See-also pub workaround_lnd_bug_4006: Option, + /// The `my_current_funding_locked` txid included in our `channel_reestablish` for the current + /// reconnect, if any. We track this as we cannot tell what was included after we've already + /// sent it, as it's possible it was unconfirmed at the time we sent it, but confirmed shortly + /// after. + funding_locked_txid_sent_in_reestablish: Option, + /// An option set when we wish to track how many ticks have elapsed while waiting for a response /// from our counterparty after entering specific states. If the peer has yet to respond after /// reaching `DISCONNECT_PEER_AWAITING_RESPONSE_TICKS`, a reconnection should be attempted to @@ -4225,6 +4232,7 @@ impl ChannelContext { announcement_sigs: None, workaround_lnd_bug_4006: None, + funding_locked_txid_sent_in_reestablish: None, sent_message_awaiting_response: None, latest_inbound_scid_alias: None, @@ -4536,6 +4544,7 @@ impl ChannelContext { announcement_sigs: None, workaround_lnd_bug_4006: None, + funding_locked_txid_sent_in_reestablish: None, sent_message_awaiting_response: None, latest_inbound_scid_alias: None, @@ -10512,6 +10521,8 @@ where // remaining cases either succeed or ErrorMessage-fail). self.context.channel_state.clear_peer_disconnected(); self.mark_response_received(); + let funding_locked_txid_sent_in_reestablish = + self.context.funding_locked_txid_sent_in_reestablish.take(); let shutdown_msg = self.get_outbound_shutdown(); @@ -10663,6 +10674,7 @@ where shutdown_msg, announcement_sigs, tx_signatures, tx_abort: None, + splice_locked: None, inferred_splice_locked: None, }); } @@ -10676,6 +10688,7 @@ where shutdown_msg, announcement_sigs, tx_signatures, tx_abort, + splice_locked: None, inferred_splice_locked: None, }); } @@ -10745,6 +10758,15 @@ where splice_txid, }) }); + let splice_locked = self.pending_splice.as_ref().and_then(|pending_splice| { + pending_splice + .sent_funding_txid + .filter(|splice_txid| Some(*splice_txid) != funding_locked_txid_sent_in_reestablish) + .map(|splice_txid| msgs::SpliceLocked { + channel_id: self.context.channel_id, + splice_txid, + }) + }); if msg.next_local_commitment_number == next_counterparty_commitment_number { if required_revoke.is_some() || self.context.signer_pending_revoke_and_ack { @@ -10763,6 +10785,7 @@ where commitment_order: self.context.resend_order.clone(), tx_signatures, tx_abort, + splice_locked, inferred_splice_locked, }) } else if msg.next_local_commitment_number == next_counterparty_commitment_number - 1 { @@ -10788,6 +10811,7 @@ where commitment_order: self.context.resend_order.clone(), tx_signatures: None, tx_abort, + splice_locked, inferred_splice_locked, }) } else { @@ -10815,6 +10839,7 @@ where commitment_order: self.context.resend_order.clone(), tx_signatures: None, tx_abort, + splice_locked, inferred_splice_locked, }) } @@ -12492,6 +12517,9 @@ where log_info!(logger, "Sending a data_loss_protect with no previous remote per_commitment_secret for channel {}", &self.context.channel_id()); [0;32] }; + let my_current_funding_locked = self.maybe_get_my_current_funding_locked(); + self.context.funding_locked_txid_sent_in_reestablish = + my_current_funding_locked.as_ref().map(|funding_locked| funding_locked.txid); msgs::ChannelReestablish { channel_id: self.context.channel_id(), // The protocol has two different commitment number concepts - the "commitment @@ -12515,7 +12543,7 @@ where your_last_per_commitment_secret: remote_last_secret, my_current_per_commitment_point: dummy_pubkey, next_funding: self.maybe_get_next_funding(), - my_current_funding_locked: self.maybe_get_my_current_funding_locked(), + my_current_funding_locked, } } @@ -17196,6 +17224,7 @@ impl<'a, 'b, 'c, ES: EntropySource, SP: SignerProvider> announcement_sigs, workaround_lnd_bug_4006: None, + funding_locked_txid_sent_in_reestablish: None, sent_message_awaiting_response: None, latest_inbound_scid_alias, diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 0ff2f19b830..4f90b00d727 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -12368,43 +12368,13 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ match peer_state.channel_by_id.entry(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 res = chan.channel_ready( - &msg, - &self.node_signer, - self.chain_hash, - &self.config.read().unwrap(), - &self.best_block.read().unwrap(), - &&logger + let res = self.internal_channel_ready_with_funded_channel( + counterparty_node_id, + msg, + chan, + &mut peer_state.pending_msg_events, ); - let announcement_sigs_opt = - try_channel_entry!(self, peer_state, res, chan_entry); - if let Some(announcement_sigs) = announcement_sigs_opt { - log_trace!(logger, "Sending announcement_signatures"); - peer_state.pending_msg_events.push(MessageSendEvent::SendAnnouncementSignatures { - node_id: counterparty_node_id.clone(), - msg: announcement_sigs, - }); - } else if chan.context.is_usable() { - // If we're sending an announcement_signatures, we'll send the (public) - // channel_update after sending a channel_announcement when we receive our - // counterparty's announcement_signatures. Thus, we only bother to send a - // channel_update here if the channel is not public, i.e. we're not sending an - // announcement_signatures. - log_trace!(logger, "Sending private initial channel_update for our counterparty"); - if let Ok((msg, _, _)) = self.get_channel_update_for_unicast(chan) { - peer_state.pending_msg_events.push(MessageSendEvent::SendChannelUpdate { - node_id: counterparty_node_id.clone(), - msg, - }); - } - } - - { - let mut pending_events = self.pending_events.lock().unwrap(); - emit_initial_channel_ready_event!(pending_events, chan); - } - + try_channel_entry!(self, peer_state, res, chan_entry); Ok(()) } else { try_channel_entry!(self, peer_state, Err(ChannelError::close( @@ -12417,6 +12387,49 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ } } + #[rustfmt::skip] + fn internal_channel_ready_with_funded_channel( + &self, counterparty_node_id: &PublicKey, msg: &msgs::ChannelReady, + chan: &mut FundedChannel, pending_msg_events: &mut Vec, + ) -> Result<(), ChannelError> { + let logger = WithChannelContext::from(&self.logger, &chan.context, None); + let announcement_sigs_opt = chan.channel_ready( + &msg, + &self.node_signer, + self.chain_hash, + &self.config.read().unwrap(), + &self.best_block.read().unwrap(), + &&logger + )?; + if let Some(announcement_sigs) = announcement_sigs_opt { + log_trace!(logger, "Sending announcement_signatures"); + pending_msg_events.push(MessageSendEvent::SendAnnouncementSignatures { + node_id: counterparty_node_id.clone(), + msg: announcement_sigs, + }); + } else if chan.context.is_usable() { + // If we're sending an announcement_signatures, we'll send the (public) + // channel_update after sending a channel_announcement when we receive our + // counterparty's announcement_signatures. Thus, we only bother to send a + // channel_update here if the channel is not public, i.e. we're not sending an + // announcement_signatures. + log_trace!(logger, "Sending private initial channel_update for our counterparty"); + if let Ok((msg, _, _)) = self.get_channel_update_for_unicast(chan) { + pending_msg_events.push(MessageSendEvent::SendChannelUpdate { + node_id: counterparty_node_id.clone(), + msg, + }); + } + } + + { + let mut pending_events = self.pending_events.lock().unwrap(); + emit_initial_channel_ready_event!(pending_events, chan); + } + + Ok(()) + } + fn internal_shutdown( &self, counterparty_node_id: &PublicKey, msg: &msgs::Shutdown, ) -> Result<(), MsgHandleErrInternal> { @@ -13240,7 +13253,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ #[rustfmt::skip] fn internal_channel_reestablish(&self, counterparty_node_id: &PublicKey, msg: &msgs::ChannelReestablish) -> Result<(), MsgHandleErrInternal> { - let (inferred_splice_locked, need_lnd_workaround, holding_cell_res) = { + let (post_splice_locked_update, holding_cell_res) = { let per_peer_state = self.per_peer_state.read().unwrap(); let peer_state_mutex = per_peer_state.get(counterparty_node_id).ok_or_else(|| { @@ -13249,7 +13262,7 @@ 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(msg.channel_id), None); let mut peer_state_lock = peer_state_mutex.lock().unwrap(); let peer_state = &mut *peer_state_lock; - match peer_state.channel_by_id.entry(msg.channel_id) { + let post_splice_locked_update = match peer_state.channel_by_id.entry(msg.channel_id) { hash_map::Entry::Occupied(mut chan_entry) => { if let Some(chan) = chan_entry.get_mut().as_funded_mut() { // Currently, we expect all holding cell update_adds to be dropped on peer @@ -13285,10 +13298,16 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ } } let need_lnd_workaround = chan.context.workaround_lnd_bug_4006.take(); - let funding_tx_signed = responses.tx_signatures.map(|tx_signatures| FundingTxSigned { - tx_signatures: Some(tx_signatures), - ..Default::default() - }); + let inferred_splice_locked = responses.inferred_splice_locked; + let funding_tx_signed = if responses.tx_signatures.is_some() || responses.splice_locked.is_some() { + Some(FundingTxSigned { + tx_signatures: responses.tx_signatures, + splice_locked: responses.splice_locked, + ..Default::default() + }) + } else { + None + }; let (htlc_forwards, decode_update_add_htlcs) = self.handle_channel_resumption( &mut peer_state.pending_msg_events, chan, responses.raa, responses.commitment_update, responses.commitment_order, Vec::new(), Vec::new(), None, responses.channel_ready, responses.announcement_sigs, @@ -13300,8 +13319,33 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ peer_state.pending_msg_events.push(upd); } - let holding_cell_res = self.check_free_peer_holding_cells(peer_state); - (responses.inferred_splice_locked, need_lnd_workaround, holding_cell_res) + if let Some(channel_ready_msg) = need_lnd_workaround { + let res = self.internal_channel_ready_with_funded_channel( + counterparty_node_id, + &channel_ready_msg, + chan, + &mut peer_state.pending_msg_events, + ); + try_channel_entry!(self, peer_state, res, chan_entry); + } + + // A reestablish may infer a missed `splice_locked`; apply it before freeing + // holding cells so we don't generate commitment updates against stale splice + // state. + if let Some(splice_locked) = inferred_splice_locked { + let result = self.internal_splice_locked_with_funded_channel( + counterparty_node_id, + &splice_locked, + chan, + &mut peer_state.in_flight_monitor_updates, + &mut peer_state.monitor_update_blocked_actions, + &mut peer_state.pending_msg_events, + peer_state.is_connected, + ); + try_channel_entry!(self, peer_state, result, chan_entry) + } else { + None + } } else { return try_channel_entry!(self, peer_state, Err(ChannelError::close( "Got a channel_reestablish message for an unfunded channel!".into())), chan_entry); @@ -13339,18 +13383,16 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ return Err(MsgHandleErrInternal::no_such_channel_for_peer(counterparty_node_id, msg.channel_id) ) } - } - }; - - self.handle_holding_cell_free_result(holding_cell_res); + }; - if let Some(channel_ready_msg) = need_lnd_workaround { - self.internal_channel_ready(counterparty_node_id, &channel_ready_msg)?; - } + let holding_cell_res = self.check_free_peer_holding_cells(peer_state); + (post_splice_locked_update, holding_cell_res) + }; - if let Some(splice_locked) = inferred_splice_locked { - self.internal_splice_locked(counterparty_node_id, &splice_locked)?; + if let Some(data) = post_splice_locked_update { + self.handle_post_monitor_update_chan_resume(data); } + self.handle_holding_cell_free_result(holding_cell_res); Ok(()) } @@ -13580,9 +13622,8 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ })?; 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) { + let post_update_data = match peer_state.channel_by_id.entry(msg.channel_id) { hash_map::Entry::Vacant(_) => { return Err(MsgHandleErrInternal::no_such_channel_for_peer( counterparty_node_id, @@ -13591,73 +13632,16 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ }, 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 result = chan.splice_locked( + let result = self.internal_splice_locked_with_funded_channel( + counterparty_node_id, msg, - &self.node_signer, - self.chain_hash, - &self.config.read().unwrap(), - self.best_block.read().unwrap().height, - &&logger, + chan, + &mut peer_state.in_flight_monitor_updates, + &mut peer_state.monitor_update_blocked_actions, + &mut peer_state.pending_msg_events, + peer_state.is_connected, ); - let splice_promotion = try_channel_entry!(self, peer_state, result, chan_entry); - if let Some(splice_promotion) = splice_promotion { - { - 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: Some( - splice_promotion.funding_txo.into_bitcoin_outpoint(), - ), - channel_type: chan.funding.get_channel_type().clone(), - }, - None, - )); - splice_promotion.discarded_funding.into_iter().for_each( - |funding_info| { - let event = Event::DiscardFunding { - channel_id: chan.context.channel_id(), - funding_info, - }; - pending_events.push_back((event, None)); - }, - ); - } - - if let Some(announcement_sigs) = splice_promotion.announcement_sigs { - log_trace!(logger, "Sending announcement_signatures",); - peer_state.pending_msg_events.push( - MessageSendEvent::SendAnnouncementSignatures { - node_id: counterparty_node_id.clone(), - msg: announcement_sigs, - }, - ); - } - - if let Some(monitor_update) = splice_promotion.monitor_update { - if let Some(data) = self.handle_new_monitor_update( - &mut peer_state.in_flight_monitor_updates, - &mut peer_state.monitor_update_blocked_actions, - &mut peer_state.pending_msg_events, - peer_state.is_connected, - chan, - splice_promotion.funding_txo, - monitor_update, - ) { - mem::drop(peer_state_lock); - mem::drop(per_peer_state); - self.handle_post_monitor_update_chan_resume(data); - } - } - } + try_channel_entry!(self, peer_state, result, chan_entry) } else { return Err(MsgHandleErrInternal::send_err_msg_no_close( "Channel is not funded, cannot splice".to_owned(), @@ -13666,10 +13650,87 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ } }, }; + mem::drop(peer_state_lock); + mem::drop(per_peer_state); + + if let Some(data) = post_update_data { + self.handle_post_monitor_update_chan_resume(data); + } Ok(()) } + fn internal_splice_locked_with_funded_channel( + &self, counterparty_node_id: &PublicKey, msg: &msgs::SpliceLocked, + chan: &mut FundedChannel, + in_flight_monitor_updates: &mut BTreeMap)>, + monitor_update_blocked_actions: &mut BTreeMap< + ChannelId, + Vec, + >, + pending_msg_events: &mut Vec, is_connected: bool, + ) -> Result, ChannelError> { + let logger = WithChannelContext::from(&self.logger, &chan.context, None); + let splice_promotion = chan.splice_locked( + msg, + &self.node_signer, + self.chain_hash, + &self.config.read().unwrap(), + self.best_block.read().unwrap().height, + &&logger, + )?; + let mut post_update_data = None; + if let Some(splice_promotion) = splice_promotion { + { + 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: Some(splice_promotion.funding_txo.into_bitcoin_outpoint()), + channel_type: chan.funding.get_channel_type().clone(), + }, + None, + )); + splice_promotion.discarded_funding.into_iter().for_each(|funding_info| { + let event = Event::DiscardFunding { + channel_id: chan.context.channel_id(), + funding_info, + }; + pending_events.push_back((event, None)); + }); + } + + if let Some(announcement_sigs) = splice_promotion.announcement_sigs { + log_trace!(logger, "Sending announcement_signatures",); + pending_msg_events.push(MessageSendEvent::SendAnnouncementSignatures { + node_id: counterparty_node_id.clone(), + msg: announcement_sigs, + }); + } + + if let Some(monitor_update) = splice_promotion.monitor_update { + post_update_data = self.handle_new_monitor_update( + in_flight_monitor_updates, + monitor_update_blocked_actions, + pending_msg_events, + is_connected, + chan, + splice_promotion.funding_txo, + monitor_update, + ); + } + } + + Ok(post_update_data) + } + /// 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 diff --git a/lightning/src/ln/splicing_tests.rs b/lightning/src/ln/splicing_tests.rs index 35c72509d0b..6e6af600faf 100644 --- a/lightning/src/ln/splicing_tests.rs +++ b/lightning/src/ln/splicing_tests.rs @@ -751,7 +751,21 @@ pub fn lock_splice<'a, 'b, 'c, 'd>( .get_monitor(splice_locked_for_node_b.channel_id) .map(|monitor| monitor.get_funding_txo().txid) .unwrap(); + complete_splice_locked_exchange( + node_a, + node_b, + splice_locked_for_node_b, + is_0conf, + expected_discard_txids, + prev_funding_txid, + ) +} +fn complete_splice_locked_exchange<'a, 'b, 'c, 'd>( + node_a: &'a Node<'b, 'c, 'd>, node_b: &'a Node<'b, 'c, 'd>, + splice_locked_for_node_b: &msgs::SpliceLocked, is_0conf: bool, expected_discard_txids: &[Txid], + prev_funding_txid: Txid, +) -> SpliceLockedResult { let node_id_a = node_a.node.get_our_node_id(); let node_id_b = node_b.node.get_our_node_id(); @@ -2585,6 +2599,83 @@ fn do_test_splice_reestablish(reload: bool, async_monitor_update: bool) { .remove_watched_txn_and_outputs(prev_funding_outpoint, prev_funding_script); } +#[test] +fn test_splice_locked_waits_for_channel_reestablish() { + // If a splice confirms after `peer_connected` but before `channel_reestablish` is handled, the + // peer state is connected while the channel still has its disconnected bit set. We must not send + // `splice_locked` until the channel is reestablished, but should send it immediately after. + 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); + + 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_value_sat = 100_000; + let (_, _, channel_id, _) = + create_announced_chan_between_nodes_with_value(&nodes, 0, 1, initial_channel_value_sat, 0); + let prev_funding_txid = get_monitor!(nodes[0], channel_id).get_funding_txo().txid; + + send_payment(&nodes[0], &[&nodes[1]], 1_000_000); + + let outputs = vec![ + TxOut { + value: Amount::from_sat(initial_channel_value_sat / 4), + script_pubkey: nodes[0].wallet_source.get_change_script().unwrap(), + }, + TxOut { + value: Amount::from_sat(initial_channel_value_sat / 4), + script_pubkey: nodes[1].wallet_source.get_change_script().unwrap(), + }, + ]; + let funding_contribution = + initiate_splice_out(&nodes[0], &nodes[1], channel_id, outputs).unwrap(); + let (splice_tx, _) = splice_channel(&nodes[0], &nodes[1], channel_id, funding_contribution); + + nodes[0].node.peer_disconnected(node_id_1); + nodes[1].node.peer_disconnected(node_id_0); + + connect_nodes(&nodes[0], &nodes[1]); + let reestablish_0 = + get_event_msg!(nodes[0], MessageSendEvent::SendChannelReestablish, node_id_1); + let reestablish_1 = + get_event_msg!(nodes[1], MessageSendEvent::SendChannelReestablish, node_id_0); + + confirm_transaction(&nodes[0], &splice_tx); + assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty()); + + nodes[1].node.handle_channel_reestablish(node_id_0, &reestablish_0); + let _ = get_event_msg!(nodes[1], MessageSendEvent::SendChannelUpdate, node_id_0); + nodes[0].node.handle_channel_reestablish(node_id_1, &reestablish_1); + let mut msg_events = nodes[0].node.get_and_clear_pending_msg_events(); + assert_eq!(msg_events.len(), 2, "{msg_events:?}"); + let splice_locked_0 = + if let MessageSendEvent::SendSpliceLocked { node_id, msg } = msg_events.remove(0) { + assert_eq!(node_id, node_id_1); + msg + } else { + panic!(); + }; + if let MessageSendEvent::SendChannelUpdate { node_id, .. } = msg_events.remove(0) { + assert_eq!(node_id, node_id_1); + } else { + panic!(); + } + + confirm_transaction(&nodes[1], &splice_tx); + complete_splice_locked_exchange( + &nodes[0], + &nodes[1], + &splice_locked_0, + false, + &[], + prev_funding_txid, + ); + + send_payment(&nodes[0], &[&nodes[1]], 1_000_000); +} + #[test] fn test_splice_confirms_on_both_sides_while_disconnected() { // Regression test: when a splice transaction confirms on both sides while peers are @@ -2703,6 +2794,91 @@ fn test_splice_confirms_on_both_sides_while_disconnected() { .remove_watched_txn_and_outputs(prev_funding_outpoint, prev_funding_script); } +#[test] +fn test_holding_cell_claim_freed_after_inferred_splice_locked() { + // If `channel_reestablish` infers a missed `splice_locked`, it must promote the splice before + // freeing holding-cell updates. If the promotion monitor update is asynchronous, holding-cell + // updates must remain held until that monitor update completes. + 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); + + 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_value_sat = 100_000; + let (_, _, channel_id, _) = + create_announced_chan_between_nodes_with_value(&nodes, 0, 1, initial_channel_value_sat, 0); + let prev_funding_outpoint = get_monitor!(nodes[0], channel_id).get_funding_txo(); + let prev_funding_script = get_monitor!(nodes[0], channel_id).get_funding_script(); + let prev_scid = nodes[0].node.list_channels()[0].short_channel_id; + + let (payment_preimage, payment_hash, ..) = route_payment(&nodes[0], &[&nodes[1]], 1_000_000); + + let outputs = vec![ + TxOut { + value: Amount::from_sat(initial_channel_value_sat / 4), + script_pubkey: nodes[0].wallet_source.get_change_script().unwrap(), + }, + TxOut { + value: Amount::from_sat(initial_channel_value_sat / 4), + script_pubkey: nodes[1].wallet_source.get_change_script().unwrap(), + }, + ]; + let funding_contribution = + initiate_splice_out(&nodes[0], &nodes[1], channel_id, outputs).unwrap(); + let (splice_tx, _) = splice_channel(&nodes[0], &nodes[1], channel_id, funding_contribution); + + nodes[0].node.peer_disconnected(node_id_1); + nodes[1].node.peer_disconnected(node_id_0); + + nodes[1].node.claim_funds(payment_preimage); + check_added_monitors(&nodes[1], 1); + expect_payment_claimed!(nodes[1], payment_hash, 1_000_000); + + confirm_transaction(&nodes[0], &splice_tx); + confirm_transaction(&nodes[1], &splice_tx); + assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty()); + assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty()); + + chanmon_cfgs[1].persister.set_update_ret(ChannelMonitorUpdateStatus::InProgress); + + let mut reconnect_args = ReconnectArgs::new(&nodes[0], &nodes[1]); + reconnect_args.expect_renegotiated_funding_locked_monitor_update = (true, true); + reconnect_args.send_announcement_sigs = (true, true); + reconnect_nodes(reconnect_args); + + expect_channel_ready_event(&nodes[0], &node_id_1); + expect_channel_ready_event(&nodes[1], &node_id_0); + assert_ne!(prev_scid, nodes[0].node.list_channels()[0].short_channel_id); + + nodes[1].chain_monitor.complete_sole_pending_chan_update(&channel_id); + chanmon_cfgs[1].persister.set_update_ret(ChannelMonitorUpdateStatus::Completed); + + let mut commitment_update = get_htlc_update_msgs(&nodes[1], &node_id_0); + check_added_monitors(&nodes[1], 1); + nodes[0] + .node + .handle_update_fulfill_htlc(node_id_1, commitment_update.update_fulfill_htlcs.remove(0)); + do_commitment_signed_dance( + &nodes[0], + &nodes[1], + &commitment_update.commitment_signed, + false, + false, + ); + + expect_payment_sent!(nodes[0], payment_preimage); + + nodes[0] + .chain_source + .remove_watched_txn_and_outputs(prev_funding_outpoint, prev_funding_script.clone()); + nodes[1] + .chain_source + .remove_watched_txn_and_outputs(prev_funding_outpoint, prev_funding_script); +} + #[test] fn test_stale_announcement_signatures_ignored_after_splice_lock() { // Regression test: a peer may transmit `announcement_signatures` signed over a pre-splice