@@ -1184,12 +1184,13 @@ impl UnfundedChannelContext {
11841184/// Info about a pending splice, used in the pre-splice channel
11851185#[cfg(splicing)]
11861186#[derive(Clone)]
1187- pub(crate) struct PendingSpliceInfoPre {
1187+ struct PendingSpliceInfoPre {
11881188 pub our_funding_contribution: i64,
11891189}
11901190
11911191#[cfg(splicing)]
11921192impl PendingSpliceInfoPre {
1193+ #[inline]
11931194 fn add_checked(base: u64, delta: i64) -> u64 {
11941195 if delta >= 0 {
11951196 base.saturating_add(delta as u64)
@@ -1241,7 +1242,7 @@ pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
12411242
12421243 /// Info about an in-progress, pending splice (if any), on the pre-splice channel
12431244 #[cfg(splicing)]
1244- pub(crate) pending_splice_pre: Option<PendingSpliceInfoPre>,
1245+ pending_splice_pre: Option<PendingSpliceInfoPre>,
12451246
12461247 latest_monitor_update_id: u64,
12471248
@@ -3648,7 +3649,8 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
36483649 }
36493650
36503651 /// Check that a balance value meets the channel reserve requirements or violates them (below reserve).
3651- /// The channel value is an input, so that this can be used for checks with new planned channel value.
3652+ /// The channel value is an input as opposed to using from self, so that this can be used in case of splicing
3653+ /// to checks with new channel value (before being comitted to it).
36523654 #[cfg(any(dual_funding, splicing))]
36533655 pub fn check_balance_meets_reserve_requirements(&self, channel_value: u64, balance: u64) -> Result<(), ChannelError> {
36543656 let holder_selected_channel_reserve_satoshis = get_v2_channel_reserve_satoshis(
@@ -4174,17 +4176,15 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
41744176
41754177 /// Get the splice_ack message that can be sent in response to splice initiation.
41764178 #[cfg(splicing)]
4177- pub fn get_splice_ack(&mut self, our_funding_contribution_satoshis: i64) -> Result<msgs::SpliceAck, ChannelError> {
4178- // TODO(splicing): checks
4179-
4179+ pub fn get_splice_ack(&self, our_funding_contribution_satoshis: i64) -> msgs::SpliceAck {
41804180 // Reuse the existing funding pubkey, in spite of the channel value changing
41814181 let funding_pubkey = self.get_holder_pubkeys().funding_pubkey;
4182- Ok( msgs::SpliceAck {
4182+ msgs::SpliceAck {
41834183 channel_id: self.channel_id,
41844184 funding_contribution_satoshis: our_funding_contribution_satoshis,
41854185 funding_pubkey,
41864186 require_confirmed_inputs: None,
4187- })
4187+ }
41884188 }
41894189}
41904190
@@ -7908,23 +7908,118 @@ impl<SP: Deref> Channel<SP> where
79087908 }
79097909 }
79107910
7911+ /// Initiate splicing
79117912 #[cfg(splicing)]
7912- pub fn splice_init<ES: Deref, L: Deref>(
7913- &mut self, our_funding_contribution_satoshis: i64,
7914- _signer_provider: &SP, _entropy_source: &ES, _holder_node_id: PublicKey, _logger: &L
7915- )
7916- -> Result<msgs::SpliceAck, ChannelError>
7917- where ES::Target: EntropySource, L::Target: Logger
7918- {
7919- if !self.context.is_outbound() {
7920- // TODO(splicing): Apply start of splice (splice_start)
7913+ pub fn splice_channel(&mut self, our_funding_contribution_satoshis: i64,
7914+ funding_feerate_perkw: u32, locktime: u32,
7915+ ) -> Result<msgs::SpliceInit, ChannelError> {
7916+ // Check if a splice has been initiated already.
7917+ // Note: this could be handled more nicely, and support multiple outstanding splice's, the incoming splice_ack matters anyways.
7918+ if let Some(splice_info) = &self.context.pending_splice_pre {
7919+ return Err(ChannelError::Warn(format!(
7920+ "Channel has already a splice pending, contribution {}", splice_info.our_funding_contribution
7921+ )));
7922+ }
79217923
7922- let splice_ack_msg = self.context.get_splice_ack(our_funding_contribution_satoshis)?;
7923- // TODO(splicing): start interactive funding negotiation
7924- Ok(splice_ack_msg)
7925- } else {
7926- Err(ChannelError::Warn("Internal consistency error: splice_init on inbound channel".into()))
7924+ if !matches!(self.context.channel_state, ChannelState::ChannelReady(_)) {
7925+ return Err(ChannelError::Warn(format!("Cannot initiate splicing, as channel is not Ready")));
7926+ }
7927+
7928+ let pre_channel_value = self.context.get_value_satoshis();
7929+ // Sanity check: capacity cannot decrease below 0
7930+ if (pre_channel_value as i64).saturating_add(our_funding_contribution_satoshis) < 0 {
7931+ return Err(ChannelError::Warn(format!(
7932+ "Post-splicing channel value cannot be negative. It was {} + {}",
7933+ pre_channel_value, our_funding_contribution_satoshis
7934+ )));
7935+ }
7936+
7937+ if our_funding_contribution_satoshis < 0 {
7938+ return Err(ChannelError::Warn(format!(
7939+ "TODO(splicing): Splice-out not supported, only splice in, contribution {}",
7940+ our_funding_contribution_satoshis,
7941+ )));
7942+ }
7943+
7944+ // Note: post-splice channel value is not yet known at this point, counterpary contribution is not known
7945+ // (Cannot test for miminum required post-splice channel value)
7946+
7947+ self.context.pending_splice_pre = Some(PendingSpliceInfoPre {
7948+ our_funding_contribution: our_funding_contribution_satoshis,
7949+ });
7950+
7951+ let msg = self.context.get_splice_init(our_funding_contribution_satoshis, funding_feerate_perkw, locktime);
7952+ Ok(msg)
7953+ }
7954+
7955+ /// Handle splice_init
7956+ #[cfg(splicing)]
7957+ pub fn splice_init(
7958+ &mut self, their_funding_contribution_satoshis: i64, our_funding_contribution_satoshis: i64,
7959+ ) -> Result<msgs::SpliceAck, ChannelError> {
7960+ // Check if a splice has been initiated already.
7961+ // Note: this could be handled more nicely, and support multiple outstanding splice's, the incoming splice_ack matters anyways.
7962+ if let Some(splice_info) = &self.context.pending_splice_pre {
7963+ return Err(ChannelError::Warn(format!(
7964+ "Channel has already a splice pending, contribution {}", splice_info.our_funding_contribution,
7965+ )));
7966+ }
7967+
7968+ if !matches!(self.context.channel_state, ChannelState::ChannelReady(_)) {
7969+ return Err(ChannelError::Warn(format!("Splicing requested on a channel that is not Ready")));
7970+ }
7971+
7972+ let pre_channel_value = self.context.get_value_satoshis();
7973+ // Sanity check: capacity cannot decrease below 0
7974+ if (pre_channel_value as i64)
7975+ .saturating_add(their_funding_contribution_satoshis)
7976+ .saturating_add(our_funding_contribution_satoshis) < 0
7977+ {
7978+ return Err(ChannelError::Warn(format!(
7979+ "Post-splicing channel value cannot be negative. It was {} + {} + {}",
7980+ pre_channel_value, their_funding_contribution_satoshis, our_funding_contribution_satoshis,
7981+ )));
7982+ }
7983+
7984+ if their_funding_contribution_satoshis.saturating_add(our_funding_contribution_satoshis) < 0 {
7985+ return Err(ChannelError::Warn(format!(
7986+ "Splice-out not supported, only splice in, relative {} + {}",
7987+ their_funding_contribution_satoshis, our_funding_contribution_satoshis,
7988+ )));
79277989 }
7990+
7991+ let post_channel_value = PendingSpliceInfoPre::compute_post_value(pre_channel_value, their_funding_contribution_satoshis, our_funding_contribution_satoshis);
7992+
7993+ // Early check for reserve requirement, assuming maximum balance of full channel value
7994+ // This will also be checked later at tx_complete
7995+ let _res = self.context.check_balance_meets_reserve_requirements(post_channel_value, post_channel_value)?;
7996+
7997+ // TODO(splicing): Apply start of splice (splice_start)
7998+
7999+ let splice_ack_msg = self.context.get_splice_ack(our_funding_contribution_satoshis);
8000+ // TODO(splicing): start interactive funding negotiation
8001+ Ok(splice_ack_msg)
8002+ }
8003+
8004+ /// Handle splice_ack
8005+ #[cfg(splicing)]
8006+ pub fn splice_ack(
8007+ &mut self, their_funding_contribution_satoshis: i64,
8008+ ) -> Result<(), ChannelError> {
8009+ // check if splice is pending
8010+ let pending_splice = if let Some(pending_splice) = &self.context.pending_splice_pre {
8011+ pending_splice
8012+ } else {
8013+ return Err(ChannelError::Warn(format!("Channel is not in pending splice")));
8014+ };
8015+
8016+ let pre_channel_value = self.context.get_value_satoshis();
8017+ let post_channel_value = PendingSpliceInfoPre::compute_post_value(pre_channel_value, pending_splice.our_funding_contribution, their_funding_contribution_satoshis);
8018+
8019+ // Early check for reserve requirement, assuming maximum balance of full channel value
8020+ // This will also be checked later at tx_complete
8021+ let _res = self.context.check_balance_meets_reserve_requirements(post_channel_value, post_channel_value)?;
8022+ Ok(())
79288023 }
79298024
79308025 // Send stuff to our remote peers:
0 commit comments