diff --git a/README.md b/README.md index e7ce5ff6f2..63a510979e 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ Client LDK Counterparty node (acceptor) handle_splice_init() - ChannelManager internal_splice_init() - ChannelManager Do checks. Check if channel ID would change. Cycle back the channel to UnfundedInboundV2 - Channel phase to RenegotiatingFundingInbound + Channel phase to RenegotiatingV2 (inbound pending) splice_start() -- ChannelContext Start the splice, update capacity, state to NegotiatingFunding, reset funding transaction get_splice_ack() -- Channel @@ -68,7 +68,7 @@ Client LDK Counterparty node (acceptor) handle_splice_ack() - ChannelManager internal_splice_ack() - ChannelManager Do checks, check against initial splice() - Channel phase to RenegotiatingFundingOutbound + Channel phase to RenegotiatingV2 (outbound pending) splice_start() -- ChannelContext Start the splice, update capacity, state to NegotiatingFunding, reset funding transaction //event: SpliceAckedInputsContributionReady diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index e780ddae1d..8e4063d78b 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -1235,6 +1235,7 @@ pub(super) enum V2Channel where SP::Target: SignerProvider { /// - NegotiatingV2(ChannelVariants) // #[cfg(any(dual_funding, splicing))] pub(super) struct ChannelVariants where SP::Target: SignerProvider { + // TODO locking funded_channels: Vec>, #[cfg(any(dual_funding, splicing))] unfunded_channel: Option>, @@ -1250,24 +1251,68 @@ impl ChannelVariants where SP::Target: SignerProvider { } } - /// Return the last funded (unconfirmed) channel - pub fn channel(&self) -> &Channel { + #[cfg(any(dual_funding, splicing))] + pub fn new_with_pending(pending_channel: V2Channel) -> Self { + Self { + funded_channels: Vec::new(), + #[cfg(any(dual_funding, splicing))] + unfunded_channel: Some(pending_channel), + } + } + + /// Return the context of the last funded (unconfirmed) channel, if any, or + /// if none, the pending one + pub fn context(&self) -> &ChannelContext { + if self.funded_channels.len() > 0 { + &self.funded_channels[self.funded_channels.len() - 1].context + } else { + #[cfg(any(dual_funding, splicing))] + if let Some(unfunded) = &self.unfunded_channel { + return unfunded.context(); + } + panic!("No channel in collection"); + } + } + + /// Return the mutable context of the last funded (unconfirmed) channel, if any, or + /// if none, the pending one + pub fn context_mut(&mut self) -> &mut ChannelContext { + if self.funded_channels.len() > 0 { + let n = self.funded_channels.len(); + &mut self.funded_channels[n - 1].context + } else { + #[cfg(any(dual_funding, splicing))] + if let Some(ref mut unfunded) = &mut self.unfunded_channel { + return unfunded.context_mut(); + } + panic!("No channel in collection"); + } + } + + /// Return the last funded (unconfirmed) channel, or if none, the pending one + pub fn get_funded_channel(&self) -> Option<&Channel> { // self.debug(); // TODO remove - debug_assert!(self.funded_channels.len() > 0); - let n = self.funded_channels.len(); - &self.funded_channels[n - 1] + if self.funded_channels.len() > 0 { + Some(&self.funded_channels[self.funded_channels.len() - 1]) + } else { + None + } } /// Return the last funded (unconfirmed) channel - pub fn channel_mut(&mut self) -> &mut Channel { + pub fn funded_channel_mut(&mut self) -> Option<&mut Channel> { self.debug(); // TODO remove - debug_assert!(self.funded_channels.len() > 0); - let n = self.funded_channels.len(); - &mut self.funded_channels[n - 1] + if self.funded_channels.len() > 0 { + let n = self.funded_channels.len(); + Some(&mut self.funded_channels[n - 1]) + } else { + None + } } /// Return all the funded channels pub fn all_funded(&mut self) -> Vec<&mut Channel> { + debug_assert!(false, "This is to be used in RBF, not yet implemented"); self.funded_channels.iter_mut().collect::>() } @@ -1285,11 +1330,32 @@ impl ChannelVariants where SP::Target: SignerProvider { } } + #[cfg(any(dual_funding, splicing))] + pub fn has_pending(&self) -> bool { + self.unfunded_channel.is_some() + } + + #[cfg(any(dual_funding, splicing))] + pub fn get_pending(&self) -> Option<&V2Channel> { + self.unfunded_channel.as_ref() + } + + #[cfg(any(dual_funding, splicing))] + pub fn get_pending_mut(&mut self) -> Option<&mut V2Channel> { + self.unfunded_channel.as_mut() + } + + #[cfg(any(dual_funding, splicing))] + pub fn take_pending(&mut self) -> Option> { + self.unfunded_channel.take() + } + #[cfg(any(dual_funding, splicing))] pub fn set_new_pending_out(&mut self, variant_channel: OutboundV2Channel) { debug_assert!(self.unfunded_channel.is_none()); self.unfunded_channel = Some(V2Channel::UnfundedOutboundV2(variant_channel)); self.debug(); // TODO remove + panic!("This is to be used in RBF, not yet implemented"); } #[cfg(any(dual_funding, splicing))] @@ -1308,8 +1374,7 @@ impl ChannelVariants where SP::Target: SignerProvider { #[cfg(any(dual_funding, splicing))] pub fn take_pending_out(&mut self) -> Option> { if self.get_pending_out_mut().is_none() { return None; } - let v = self.unfunded_channel.take(); - match v { + match self.unfunded_channel.take() { None => panic!("None"), Some(ch) => { match ch { @@ -1325,6 +1390,7 @@ impl ChannelVariants where SP::Target: SignerProvider { debug_assert!(self.unfunded_channel.is_none()); self.unfunded_channel = Some(V2Channel::UnfundedInboundV2(variant_channel)); self.debug(); // TODO remove + panic!("This is to be used in RBF, not yet implemented"); } #[cfg(any(dual_funding, splicing))] @@ -1356,11 +1422,14 @@ impl ChannelVariants where SP::Target: SignerProvider { } /// Take the last funded (unconfirmed) channel - pub fn take_channel(&mut self) -> Channel { + pub fn take_funded_channel(&mut self) -> Option> { // self.debug(); // TODO remove - debug_assert!(self.funded_channels.len() > 0); - let n = self.funded_channels.len(); - self.funded_channels.remove(n - 1) + if self.funded_channels.len() > 0 { + let n = self.funded_channels.len(); + Some(self.funded_channels.remove(n - 1)) + } else { + None + } } /// Keep only the one confirmed channel, drop the other variants @@ -1368,7 +1437,6 @@ impl ChannelVariants where SP::Target: SignerProvider { pub fn keep_one_confirmed(&mut self, channel_index: usize) { self.debug(); // TODO remove if self.funded_channels.len() > 1 { - println!("QQQ ChannelVariants keep_one_confirmed collapsing {} to {}", self.funded_channels.len(), channel_index); debug_assert!(channel_index < self.funded_channels.len()); self.funded_channels = vec![self.funded_channels.remove(channel_index)]; #[cfg(any(dual_funding, splicing))] @@ -1378,6 +1446,140 @@ impl ChannelVariants where SP::Target: SignerProvider { self.debug(); // TODO remove } } + + #[cfg(any(dual_funding, splicing))] + pub fn tx_add_input(&mut self, msg: &msgs::TxAddInput) -> Result { + if let Some(pending) = self.get_pending_mut() { + Ok(pending.tx_add_input(msg)) + } else { + // funded + Err(ChannelError::Warn(format!("Channel is already funded, not expecting tx_add_input"))) + } + } + + #[cfg(any(dual_funding, splicing))] + pub fn tx_add_output(&mut self, msg: &msgs::TxAddOutput) -> Result { + if let Some(pending) = self.get_pending_mut() { + Ok(pending.tx_add_output(msg)) + } else { + // funded + Err(ChannelError::Warn(format!("Channel is already funded, not expecting tx_add_output"))) + } + } + + #[cfg(any(dual_funding, splicing))] + pub fn tx_complete(&mut self, msg: &msgs::TxComplete) -> Result { + if let Some(pending) = self.get_pending_mut() { + Ok(pending.tx_complete(msg)) + } else { + // funded + Err(ChannelError::Warn(format!("Channel is already funded, not expecting tx_complete"))) + } + } + + #[cfg(any(dual_funding, splicing))] + pub fn funding_tx_constructed( + mut self, counterparty_node_id: &PublicKey, signing_session: InteractiveTxSigningSession, logger: &L + ) -> Result<(Channel, msgs::CommitmentSigned, Option), (Self, ChannelError)> + where + L::Target: Logger + { + if let Some(pending) = self.take_pending() { + pending.funding_tx_constructed(counterparty_node_id, signing_session, logger) + .map_err(|(_ch, err)| (self, err)) + } else { + Err((self, ChannelError::Close(format!("Channel is already funded, not expecting tx_constructed")))) + } + } + + #[cfg(any(dual_funding, splicing))] + pub fn funding_transaction_signed(&mut self, channel_id: &ChannelId, witnesses: Vec) -> Result, ChannelError> { + if let Some(funded) = self.funded_channel_mut() { + funded.funding_transaction_signed(channel_id, witnesses) + } else { + Err(ChannelError::Close(format!("Channel with id {} is has no funded channel, not expecting funding signatures", channel_id))) + } + } + + #[cfg(any(dual_funding, splicing))] + pub fn commitment_signed_initial_v2(&mut self, msg: &msgs::CommitmentSigned, best_block: BestBlock, signer_provider: &SP, logger: &L) + -> Result<(&mut Channel, ChannelMonitor<::EcdsaSigner>), ChannelError> + where L::Target: Logger + { + if let Some(post_chan) = self.funded_channel_mut() { + // TODO(splicing): handle on pre as well + // TODO(splicing): For splice pending case expand this condition, depending if commitment was already received or not + let interactive_tx_signing_in_progress = post_chan.interactive_tx_signing_session.is_some(); + if interactive_tx_signing_in_progress { + let monitor = post_chan.commitment_signed_initial_v2(&msg, best_block, signer_provider, logger)?; + Ok((post_chan, monitor)) + } else { + Err(ChannelError::Close("Got a commitment_signed message, but there is no transaction negotiation context!".into())) + } + } else { + Err(ChannelError::Close("Got a commitment_signed message, but there is no funded channel!".into())) + } + } + + #[cfg(splicing)] + pub fn splice_init( + &mut self, our_funding_contribution_satoshis: i64, + signer_provider: &SP, entropy_source: &ES, holder_node_id: PublicKey, logger: &L + ) + -> Result + where ES::Target: EntropySource, L::Target: Logger + { + if let Some(post_chan) = self.get_pending_mut() { + if !post_chan.is_outbound() { + // Apply start of splice change in the state + post_chan.context_mut().splice_start(false, logger); + let splice_ack_msg = post_chan.get_splice_ack(our_funding_contribution_satoshis)?; + let _msg = post_chan.begin_interactive_funding_tx_construction(signer_provider, entropy_source, holder_node_id) + .map_err(|err| ChannelError::Warn(format!("Failed to start interactive transaction construction, {:?}", err)))?; + Ok(splice_ack_msg) + } else { + Err(ChannelError::Warn("Internal consistency error: splice_init but no inbound channel".into())) + } + } else { + Err(ChannelError::Warn("Internal consistency error: splice_init and no pending channel".into())) + } + } + + #[cfg(splicing)] + pub fn splice_ack( + &mut self, + signer_provider: &SP, entropy_source: &ES, holder_node_id: PublicKey, logger: &L + ) + -> Result, ChannelError> + where ES::Target: EntropySource, L::Target: Logger + { + if let Some(post_chan) = self.get_pending_mut() { + if post_chan.is_outbound() { + // Apply start of splice change in the state + post_chan.context_mut().splice_start(true, logger); + + /* Note: SpliceAckedInputsContributionReady event is no longer used + // Prepare SpliceAckedInputsContributionReady event + let mut pending_events = self.pending_events.lock().unwrap(); + pending_events.push_back((events::Event::SpliceAckedInputsContributionReady { + channel_id: post_chan_id, + counterparty_node_id: *counterparty_node_id, + pre_channel_value_satoshis: pre_channel_value, + post_channel_value_satoshis: post_channel_value, + holder_funding_satoshis: if post_channel_value < pre_channel_value { 0 } else { post_channel_value.saturating_sub(pre_channel_value) }, + counterparty_funding_satoshis: 0, + } , None)); + */ + let tx_msg_opt = post_chan.begin_interactive_funding_tx_construction(signer_provider, entropy_source, holder_node_id) + .map_err(|err| ChannelError::Warn(format!("V2 channel rejected due to sender error, {:?}", err)))?; + Ok(tx_msg_opt) + } else { + Err(ChannelError::Warn("Internal consistency error: splice_ack but no outbound channel".into())) + } + } else { + Err(ChannelError::Warn("Internal consistency error: splice_ack but no pending channel".into())) + } + } } /// The `ChannelPhase` enum describes the current phase in life of a lightning channel with each of @@ -1391,19 +1593,11 @@ pub(super) enum ChannelPhase where SP::Target: SignerProvider { UnfundedInboundV2(InboundV2Channel), /// Funding transaction negotiated (pending or locked) Funded(Channel), - /// Renegotiating existing channel, outbound (initiated by us) - /// Contains already negotiated channel (funded, pre-splice) and - /// channel being negotiated (post-splice) - #[cfg(splicing)] - RenegotiatingFundingOutbound((Channel, V2Channel)), - /// Renegotiating existing channel, inbound (initiated by the peer) - /// Contains already negotiated channel (funded, pre-splice) and - /// channel being negotiated (post-splice) - #[cfg(splicing)] - RenegotiatingFundingInbound((Channel, V2Channel)), - /// Renegotiating existing channel, new channel is negotiated but still pending. - /// Contains the old channel (funded, pre-splice) and - /// and the new channel, that is negotiated but pending (funded, post-splice) + /// Renegotiating existing channel, for splicing + /// First channel is the already funded (and confirmed, pre-splice) channel. + /// The second collection can hold: + /// - the negotiated (funded) but unconfirmed post-splice channel + /// - the channel being negotiated (post-splice, inbound or outbound) #[cfg(splicing)] RenegotiatingV2((Channel, ChannelVariants)), } @@ -1421,13 +1615,9 @@ impl<'a, SP: Deref> ChannelPhase where ChannelPhase::UnfundedOutboundV2(chan) => &chan.context, #[cfg(any(dual_funding, splicing))] ChannelPhase::UnfundedInboundV2(chan) => &chan.context, - #[cfg(splicing)] - ChannelPhase::RenegotiatingFundingOutbound((_pre_chan, post_chan)) => post_chan.context(), - #[cfg(splicing)] - ChannelPhase::RenegotiatingFundingInbound((_pre_chan, post_chan)) => post_chan.context(), // Both post and pre exist #[cfg(splicing)] - ChannelPhase::RenegotiatingV2((_pre_chan, post_chans)) => &post_chans.channel().context, + ChannelPhase::RenegotiatingV2((_pre_chan, post_chans)) => post_chans.context(), } } @@ -1440,13 +1630,9 @@ impl<'a, SP: Deref> ChannelPhase where ChannelPhase::UnfundedOutboundV2(ref mut chan) => &mut chan.context, #[cfg(any(dual_funding, splicing))] ChannelPhase::UnfundedInboundV2(ref mut chan) => &mut chan.context, - #[cfg(splicing)] - ChannelPhase::RenegotiatingFundingOutbound((ref mut _pre_chan, post_chan)) => post_chan.context_mut(), - #[cfg(splicing)] - ChannelPhase::RenegotiatingFundingInbound((ref mut _pre_chan, post_chan)) => post_chan.context_mut(), // Both post and pre exist #[cfg(splicing)] - ChannelPhase::RenegotiatingV2((ref mut _pre_chan, post_chans)) => &mut post_chans.channel_mut().context, + ChannelPhase::RenegotiatingV2((_pre_chan, ref mut post_chans)) => post_chans.context_mut(), } } } @@ -1472,7 +1658,6 @@ impl UnfundedChannelContext { self.unfunded_channel_age_ticks >= UNFUNDED_CHANNEL_AGE_LIMIT_TICKS } - #[cfg(splicing)] pub fn default() -> Self { Self { unfunded_channel_age_ticks: 0 } } } diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index aae313129f..1b4b51a8c6 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -942,11 +942,14 @@ impl PeerState where SP::Target: SignerProvider { #[cfg(any(dual_funding, splicing))] ChannelPhase::UnfundedInboundV2(_) => false, #[cfg(splicing)] - ChannelPhase::RenegotiatingFundingOutbound(_chan) => true, - #[cfg(splicing)] - ChannelPhase::RenegotiatingFundingInbound(_chan) => false, - #[cfg(splicing)] - ChannelPhase::RenegotiatingV2(_) => true, + ChannelPhase::RenegotiatingV2((_pre_chan, post_chans)) => { + if let Some(pending) = post_chans.get_pending() { + pending.is_outbound() + } else { + // funded + true + } + }, } ) && self.monitor_update_blocked_actions.is_empty() @@ -2824,11 +2827,20 @@ macro_rules! convert_chan_phase_err { convert_chan_phase_err!($self, $err, channel, $channel_id, UNFUNDED_CHANNEL) }, #[cfg(splicing)] - ChannelPhase::RenegotiatingFundingOutbound(_chan) | - ChannelPhase::RenegotiatingFundingInbound(_chan) => todo!("splicing"), - #[cfg(splicing)] ChannelPhase::RenegotiatingV2((_, channels)) => { - convert_chan_phase_err!($self, $err, channels.channel_mut(), $channel_id, FUNDED_CHANNEL) + if let Some(funded_channel) = channels.funded_channel_mut() { + convert_chan_phase_err!($self, $err, funded_channel, $channel_id, FUNDED_CHANNEL) + } else { + match channels.get_pending_mut() { + Some(V2Channel::UnfundedOutboundV2(ch)) => { + convert_chan_phase_err!($self, $err, ch, $channel_id, UNFUNDED_CHANNEL) + }, + Some(V2Channel::UnfundedInboundV2(ch)) => { + convert_chan_phase_err!($self, $err, ch, $channel_id, UNFUNDED_CHANNEL) + }, + None => panic!("No channel"), + } + } }, } }; @@ -3932,8 +3944,6 @@ where (None, chan_phase.context().get_counterparty_node_id()) }, #[cfg(splicing)] - ChannelPhase::RenegotiatingFundingOutbound(_) | - ChannelPhase::RenegotiatingFundingInbound(_) | ChannelPhase::RenegotiatingV2(_) => todo!("splicing"), } } else if peer_state.inbound_channel_request_by_id.remove(channel_id).is_some() { @@ -5116,10 +5126,11 @@ where }); } }, + #[cfg(splicing)] Some(ChannelPhase::RenegotiatingV2((_, chans))) => { - let tx_signatures_opt = chans.channel_mut().funding_transaction_signed(channel_id, witnesses) + let tx_signatures_opt = chans.funding_transaction_signed(channel_id, witnesses) .map_err(|_err| APIError::APIMisuseError { - err: format!("Channel with id {} has no pending signing session, not expecting funding signatures", channel_id) + err: format!("Channel with id {} not expecting funding signatures", channel_id) })?; if let Some(tx_signatures) = tx_signatures_opt { peer_state.pending_msg_events.push(events::MessageSendEvent::SendTxSignatures { @@ -6309,8 +6320,6 @@ where pending_msg_events, counterparty_node_id) }, #[cfg(splicing)] - ChannelPhase::RenegotiatingFundingOutbound(_) | - ChannelPhase::RenegotiatingFundingInbound(_) | ChannelPhase::RenegotiatingV2(_) => todo!("splicing"), } }); @@ -7641,8 +7650,6 @@ where continue; }, #[cfg(splicing)] - ChannelPhase::RenegotiatingFundingOutbound(_) | - ChannelPhase::RenegotiatingFundingInbound(_) | ChannelPhase::RenegotiatingV2(_) => todo!("splicing"), } } @@ -8095,9 +8102,15 @@ where channel.tx_add_input(msg).into_msg_send_event(counterparty_node_id) }, #[cfg(splicing)] - ChannelPhase::RenegotiatingFundingInbound((ref _pre_chan, ref mut post_chan)) | - ChannelPhase::RenegotiatingFundingOutbound((ref _pre_chan, ref mut post_chan)) => { - post_chan.tx_add_input(msg).into_msg_send_event(counterparty_node_id) + ChannelPhase::RenegotiatingV2((ref _pre_chan, ref mut post_chans)) => { + match post_chans.tx_add_input(msg) { + Ok(ia_res) => ia_res.into_msg_send_event(counterparty_node_id), + Err(err) => { + try_chan_phase_entry!(self, Err(ChannelError::Warn( + format!("Error while tx_add_input: {:?}", err) + )), chan_phase_entry) + } + } }, _ => try_chan_phase_entry!(self, Err(ChannelError::Warn( "Got a tx_add_input message with no interactive transaction construction expected or in-progress" @@ -8135,9 +8148,15 @@ where channel.tx_add_output(msg).into_msg_send_event(counterparty_node_id) }, #[cfg(splicing)] - ChannelPhase::RenegotiatingFundingInbound((ref _pre_chan, ref mut post_chan)) | - ChannelPhase::RenegotiatingFundingOutbound((ref _pre_chan, ref mut post_chan)) => { - post_chan.tx_add_output(msg).into_msg_send_event(counterparty_node_id) + ChannelPhase::RenegotiatingV2((ref _pre_chan, ref mut post_chans)) => { + match post_chans.tx_add_output(msg) { + Ok(ia_res) => ia_res.into_msg_send_event(counterparty_node_id), + Err(err) => { + try_chan_phase_entry!(self, Err(ChannelError::Warn( + format!("Error while tx_add_output: {:?}", err) + )), chan_phase_entry) + } + } }, _ => try_chan_phase_entry!(self, Err(ChannelError::Warn( "Got a tx_add_output message with no interactive transaction construction expected or in-progress" @@ -8245,9 +8264,15 @@ where channel.tx_complete(msg).into_msg_send_event(counterparty_node_id) }, #[cfg(splicing)] - ChannelPhase::RenegotiatingFundingInbound((_pre_chan, post_chan)) | - ChannelPhase::RenegotiatingFundingOutbound((_pre_chan, post_chan)) => { - post_chan.tx_complete(msg).into_msg_send_event(counterparty_node_id) + ChannelPhase::RenegotiatingV2((_pre_chan, post_chans)) => { + match post_chans.tx_complete(msg) { + Ok(ia_res) => ia_res.into_msg_send_event(counterparty_node_id), + Err(err) => { + try_chan_phase_entry!(self, Err(ChannelError::Warn( + format!("Error while tx_complete message: {:?}", err) + )), chan_phase_entry) + } + } }, _ => try_chan_phase_entry!(self, Err(ChannelError::Close( "Got a tx_complete message with no interactive transaction construction expected or in-progress" @@ -8265,7 +8290,7 @@ where |(chan, err)| { (ChannelPhase::UnfundedOutboundV2(chan), err) } - ), None) + ), None::>) }, ChannelPhase::UnfundedInboundV2(chan) => { (chan.funding_tx_constructed(counterparty_node_id, signing_session, &self.logger).map_err( @@ -8275,17 +8300,10 @@ where ), None) }, #[cfg(splicing)] - ChannelPhase::RenegotiatingFundingInbound((pre_chan, post_chan)) => { - match post_chan.funding_tx_constructed(counterparty_node_id, signing_session, &self.logger) { + ChannelPhase::RenegotiatingV2((pre_chan, post_chans)) => { + match post_chans.funding_tx_constructed(counterparty_node_id, signing_session, &self.logger) { Ok(r) => (Ok(r), Some(pre_chan)), - Err((chan, err)) => (Err((ChannelPhase::RenegotiatingFundingInbound((pre_chan, chan)), err)), None), - } - }, - #[cfg(splicing)] - ChannelPhase::RenegotiatingFundingOutbound((pre_chan, post_chan)) => { - match post_chan.funding_tx_constructed(counterparty_node_id, signing_session, &self.logger) { - Ok(r) => (Ok(r), Some(pre_chan)), - Err((chan, err)) => (Err((ChannelPhase::RenegotiatingFundingOutbound((pre_chan, chan)), err)), None), + Err((chans, err)) => (Err((ChannelPhase::RenegotiatingV2((pre_chan, chans)), err)), None), } }, _ => (Err((channel_phase, ChannelError::Warn( @@ -8311,12 +8329,13 @@ where }); channel.set_next_funding_txid(&funding_txid); if let Some(pre_chan) = pre_splice_chan { + #[cfg(splicing)] peer_state.channel_by_id.insert(channel_id.clone(), ChannelPhase::RenegotiatingV2((pre_chan, ChannelVariants::new(channel)))); } else { peer_state.channel_by_id.insert(channel_id.clone(), ChannelPhase::Funded(channel)); } }, - Err((channel_phase, _)) => { + Err((channel_phase, _err)) => { peer_state.channel_by_id.insert(channel_id, channel_phase); return Err(MsgHandleErrInternal::send_err_msg_no_close(format!("{}", counterparty_node_id), msg.channel_id)) }, @@ -8348,9 +8367,18 @@ where let channel_phase = chan_phase_entry.get_mut(); let chan = match channel_phase { ChannelPhase::Funded(chan) => chan, - ChannelPhase::RenegotiatingV2((_, chans)) => { chans.channel_mut() }, + #[cfg(splicing)] + ChannelPhase::RenegotiatingV2((_, chans)) => { + if let Some(funded) = chans.funded_channel_mut() { + funded + } else { + try_chan_phase_entry!(self, Err(ChannelError::Close( + "Got tx_signatures while renegotiating but no funded channel" + .into())), chan_phase_entry) + } + }, _ => try_chan_phase_entry!(self, Err(ChannelError::Close( - "Got an unexpected tx_signatures message" + "Got tx_signatures message with no funded channel" .into())), chan_phase_entry) }; let (tx_signatures_opt, funding_tx_opt) = try_chan_phase_entry!(self, chan.tx_signatures(&msg, &self.logger), chan_phase_entry); @@ -8468,7 +8496,14 @@ where hash_map::Entry::Occupied(mut chan_phase_entry) => { match chan_phase_entry.get_mut() { // Note: no need to cover Funded - ChannelPhase::RenegotiatingV2((_, _chans)) => {}, + ChannelPhase::RenegotiatingV2((_, chans)) => { + if let Some(_funded) = chans.get_funded_channel() { + // OK, noop + } else { + return try_chan_phase_entry!(self, Err(ChannelError::Close( + "Got a splice_locked message while renegotiating, but there is no funded channel!".into())), chan_phase_entry); + } + }, _ => { return try_chan_phase_entry!(self, Err(ChannelError::Close( "Got a splice_locked message for an unfunded channel!".into())), chan_phase_entry); @@ -8484,7 +8519,13 @@ where let phase = peer_state.channel_by_id.remove(&msg.channel_id); let chan = match phase { // Some(ChannelPhase::Funded(chan)) => chan, - Some(ChannelPhase::RenegotiatingV2((_, mut chans))) => chans.take_channel(), + Some(ChannelPhase::RenegotiatingV2((_, mut chans))) => { + if let Some(funded) = chans.take_funded_channel() { + funded + } else { + return Err(MsgHandleErrInternal::send_err_msg_no_close("Internal error TODO".into(), msg.channel_id)); + } + }, _ => { return Err(MsgHandleErrInternal::send_err_msg_no_close("Internal error TODO".into(), msg.channel_id)); }, @@ -8601,8 +8642,6 @@ where finish_shutdown = Some(chan.context_mut().force_shutdown(false, ClosureReason::CounterpartyCoopClosedUnfundedChannel)); }, #[cfg(splicing)] - ChannelPhase::RenegotiatingFundingOutbound(_) | - ChannelPhase::RenegotiatingFundingInbound(_) | ChannelPhase::RenegotiatingV2(_) => todo!("splicing"), } } else { @@ -8918,22 +8957,9 @@ where }, #[cfg(splicing)] ChannelPhase::RenegotiatingV2((_pre_chan, post_chans)) => { - let post_chan = post_chans.channel_mut(); - // TODO(splicing): handle on pre as well - let logger = WithChannelContext::from(&self.logger, &post_chan.context); - let post_funding_txo = post_chan.context.get_funding_txo().unwrap(); - - #[cfg(any(dual_funding, splicing))] - // TODO(splicing): For splice pending case expand this condition, depending if commitment was already received or not - let interactive_tx_signing_in_progress = post_chan.interactive_tx_signing_session.is_some(); - #[cfg(not(any(dual_funding, splicing)))] - let interactive_tx_signing_in_progress = false; - - if interactive_tx_signing_in_progress { - #[cfg(any(dual_funding, splicing))] - { - let monitor = try_chan_phase_entry!(self, - post_chan.commitment_signed_initial_v2(&msg, best_block, &self.signer_provider, &&logger), chan_phase_entry); + match post_chans.commitment_signed_initial_v2(&msg, best_block, &self.signer_provider, &self.logger) { + Ok((post_chan, monitor)) => { + let post_funding_txo = post_chan.context.get_funding_txo().unwrap(); if let Ok(persist_status) = self.chain_monitor.watch_channel(post_funding_txo, monitor) { if let Some(tx_signatures) = post_chan.interactive_tx_signing_session.as_mut().map(|session| session.received_commitment_signed(msg.clone())).flatten() { // At this point we have received a commitment signed and we are watching the channel so @@ -8944,18 +8970,19 @@ where }); } handle_new_monitor_update!(self, persist_status, peer_state_lock, peer_state, per_peer_state, post_chan, INITIAL_MONITOR); + Ok(()) } else { try_chan_phase_entry!(self, Err(ChannelError::Close("Channel funding outpoint was a duplicate".to_owned())), chan_phase_entry) } } + Err(err) => try_chan_phase_entry!(self, Err(err), chan_phase_entry), } - Ok(()) }, - _ => return try_chan_phase_entry!(self, Err(ChannelError::Close( + _ => try_chan_phase_entry!(self, Err(ChannelError::Close( "Got a commitment_signed message for an unfunded channel!".into())), chan_phase_entry), } }, - 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::Vacant(_) => 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)) } } @@ -9454,34 +9481,32 @@ where // Add the modified channel let post_chan_id = post_chan.context().channel_id(); - peer_state.channel_by_id.insert(post_chan_id, ChannelPhase::RenegotiatingFundingInbound((prev_chan, post_chan))); + peer_state.channel_by_id.insert(post_chan_id, ChannelPhase::RenegotiatingV2( + (prev_chan, ChannelVariants::new_with_pending(post_chan)) + )); // Perform state changes match peer_state.channel_by_id.entry(post_chan_id) { hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close("Internal consistency error".to_string(), post_chan_id)), hash_map::Entry::Occupied(mut chan_entry) => { - if let ChannelPhase::RenegotiatingFundingInbound((_pre_chan, post_chan)) = chan_entry.get_mut() { - // Apply start of splice change in the state - post_chan.context_mut().splice_start(false, &self.logger); - - let new_msg = post_chan.get_splice_ack(our_funding_contribution) - .map_err(|e| MsgHandleErrInternal::from_chan_no_close(e, post_chan.context().channel_id()))?; - peer_state.pending_msg_events.push(events::MessageSendEvent::SendSpliceAck { - node_id: *counterparty_node_id, - msg: new_msg, - }); - - let _msg = post_chan.begin_interactive_funding_tx_construction(&self.signer_provider, &self.entropy_source, self.get_our_node_id()) - .map_err(|e| MsgHandleErrInternal::send_err_msg_no_close( - format!("Failed to start interactive transaction construction, {:?}", e), msg.channel_id - ))?; + if let ChannelPhase::RenegotiatingV2((_pre_chan, post_chans)) = chan_entry.get_mut() { + match post_chans.splice_init(our_funding_contribution, &self.signer_provider, &self.entropy_source, self.get_our_node_id(), &self.logger) { + Ok(splice_ack_msg) => { + peer_state.pending_msg_events.push(events::MessageSendEvent::SendSpliceAck { + node_id: *counterparty_node_id, + msg: splice_ack_msg, + }); + Ok(()) + }, + Err(err) => { + Err(MsgHandleErrInternal::from_chan_no_close(err, post_chan_id)) + } + } } else { - return Err(MsgHandleErrInternal::send_err_msg_no_close("Internal consistency error".to_string(), post_chan_id)); + Err(MsgHandleErrInternal::send_err_msg_no_close("Internal consistency error: splice_init while not renegotiating".to_string(), post_chan_id)) } } } - - Ok(()) } // #SPLICING STEP5 I @@ -9549,50 +9574,39 @@ where // Add the modified channel let post_chan_id = post_chan.context().channel_id(); - peer_state.channel_by_id.insert(post_chan_id, ChannelPhase::RenegotiatingFundingOutbound((prev_chan, post_chan))); + peer_state.channel_by_id.insert(post_chan_id, ChannelPhase::RenegotiatingV2( + (prev_chan, ChannelVariants::new_with_pending(post_chan)) + )); // Perform state changes match peer_state.channel_by_id.entry(post_chan_id) { hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close("Internal consistency error".to_string(), post_chan_id)), hash_map::Entry::Occupied(mut chan_entry) => { - if let ChannelPhase::RenegotiatingFundingOutbound((_pre_chan, post_chan)) = chan_entry.get_mut() { - // Apply start of splice change in the state - post_chan.context_mut().splice_start(true, &self.logger); - - /* Note: SpliceAckedInputsContributionReady event is no longer used - // Prepare SpliceAckedInputsContributionReady event - let mut pending_events = self.pending_events.lock().unwrap(); - pending_events.push_back((events::Event::SpliceAckedInputsContributionReady { - channel_id: post_chan_id, - counterparty_node_id: *counterparty_node_id, - pre_channel_value_satoshis: pre_channel_value, - post_channel_value_satoshis: post_channel_value, - holder_funding_satoshis: if post_channel_value < pre_channel_value { 0 } else { post_channel_value.saturating_sub(pre_channel_value) }, - counterparty_funding_satoshis: 0, - } , None)); - */ - let tx_msg_opt = post_chan.begin_interactive_funding_tx_construction(&self.signer_provider, - &self.entropy_source, self.get_our_node_id()) - .map_err(|e| MsgHandleErrInternal::from_chan_no_close( - ChannelError::Close(format!("V2 channel rejected due to sender error {:?}", e)), post_chan_id))?; - if let Some(tx_msg) = tx_msg_opt { - let msg_send_event = match tx_msg { - InteractiveTxMessageSend::TxAddInput(msg) => events::MessageSendEvent::SendTxAddInput { - node_id: *counterparty_node_id, msg }, - InteractiveTxMessageSend::TxAddOutput(msg) => events::MessageSendEvent::SendTxAddOutput { - node_id: *counterparty_node_id, msg }, - InteractiveTxMessageSend::TxComplete(msg) => events::MessageSendEvent::SendTxComplete { - node_id: *counterparty_node_id, msg }, - }; - peer_state.pending_msg_events.push(msg_send_event); + if let ChannelPhase::RenegotiatingV2((_pre_chan, post_chans)) = chan_entry.get_mut() { + match post_chans.splice_ack(&self.signer_provider, &self.entropy_source, self.get_our_node_id(), &self.logger) { + Ok(tx_msg_opt) => { + if let Some(tx_msg) = tx_msg_opt { + let msg_send_event = match tx_msg { + InteractiveTxMessageSend::TxAddInput(msg) => events::MessageSendEvent::SendTxAddInput { + node_id: *counterparty_node_id, msg }, + InteractiveTxMessageSend::TxAddOutput(msg) => events::MessageSendEvent::SendTxAddOutput { + node_id: *counterparty_node_id, msg }, + InteractiveTxMessageSend::TxComplete(msg) => events::MessageSendEvent::SendTxComplete { + node_id: *counterparty_node_id, msg }, + }; + peer_state.pending_msg_events.push(msg_send_event); + } + Ok(()) + }, + Err(err) => { + Err(MsgHandleErrInternal::from_chan_no_close(err, post_chan_id)) + }, } } else { - return Err(MsgHandleErrInternal::send_err_msg_no_close("Internal consistency error".to_string(), post_chan_id)); + Err(MsgHandleErrInternal::send_err_msg_no_close("Internal consistency error: splice_ack while not renegotiating".to_string(), post_chan_id)) } } } - - Ok(()) } /// Process pending events from the [`chain::Watch`], returning whether any events were processed. @@ -10851,12 +10865,16 @@ where #[cfg(any(dual_funding, splicing))] ChannelPhase::UnfundedOutboundV2(_) | ChannelPhase::UnfundedInboundV2(_) => { return true }, ChannelPhase::Funded(chan) => { chan } - #[cfg(splicing)] - ChannelPhase::RenegotiatingFundingOutbound(_) | - ChannelPhase::RenegotiatingFundingInbound(_) => todo!("splicing"), // Both post and pre exist #[cfg(splicing)] - ChannelPhase::RenegotiatingV2((_, post_chans)) => { post_chans.channel_mut() } + ChannelPhase::RenegotiatingV2((_, post_chans)) => { + if let Some(funded) = post_chans.funded_channel_mut() { + funded + } else { + // no funded + todo!("splicing"); + } + }, }; let res = f(channel); if let Ok((channel_ready_opt, splice_locked_opt, mut timed_out_pending_htlcs, announcement_sigs)) = res { @@ -11386,8 +11404,6 @@ where &mut chan.context }, #[cfg(splicing)] - ChannelPhase::RenegotiatingFundingOutbound(_) | - ChannelPhase::RenegotiatingFundingInbound(_) | ChannelPhase::RenegotiatingV2(_) => todo!("splicing"), }; // Clean up for removal. @@ -11572,8 +11588,6 @@ where }, #[cfg(splicing)] - ChannelPhase::RenegotiatingFundingOutbound(_) | - ChannelPhase::RenegotiatingFundingInbound(_) | ChannelPhase::RenegotiatingV2(_) => todo!("splicing"), } } @@ -11685,8 +11699,6 @@ where #[cfg(any(dual_funding, splicing))] Some(ChannelPhase::UnfundedInboundV2(_)) => (), #[cfg(splicing)] - Some(ChannelPhase::RenegotiatingFundingOutbound(_)) | - Some(ChannelPhase::RenegotiatingFundingInbound(_)) | Some(ChannelPhase::RenegotiatingV2(_)) => todo!("splicing"), } } diff --git a/lightning/src/ln/functional_tests_splice.rs b/lightning/src/ln/functional_tests_splice.rs index 2d4be6eb5a..9939ebdd61 100644 --- a/lightning/src/ln/functional_tests_splice.rs +++ b/lightning/src/ln/functional_tests_splice.rs @@ -79,7 +79,7 @@ fn get_funding_key(node: &Node, counterparty_node: &Node, channel_id: &ChannelId let local_chan = chan_lock.channel_by_id.get(&channel_id).map( |phase| match phase { ChannelPhase::Funded(chan) => Some(chan), - ChannelPhase::RenegotiatingV2((_, chans)) => Some(chans.channel()), + ChannelPhase::RenegotiatingV2((_, chans)) => chans.get_funded_channel(), _ => None, } ).flatten().unwrap();