@@ -1184,12 +1184,13 @@ impl UnfundedChannelContext {
1184
1184
/// Info about a pending splice, used in the pre-splice channel
1185
1185
#[cfg(splicing)]
1186
1186
#[derive(Clone)]
1187
- pub(crate) struct PendingSpliceInfoPre {
1187
+ struct PendingSpliceInfoPre {
1188
1188
pub our_funding_contribution: i64,
1189
1189
}
1190
1190
1191
1191
#[cfg(splicing)]
1192
1192
impl PendingSpliceInfoPre {
1193
+ #[inline]
1193
1194
fn add_checked(base: u64, delta: i64) -> u64 {
1194
1195
if delta >= 0 {
1195
1196
base.saturating_add(delta as u64)
@@ -1241,7 +1242,7 @@ pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
1241
1242
1242
1243
/// Info about an in-progress, pending splice (if any), on the pre-splice channel
1243
1244
#[cfg(splicing)]
1244
- pub(crate) pending_splice_pre: Option<PendingSpliceInfoPre>,
1245
+ pending_splice_pre: Option<PendingSpliceInfoPre>,
1245
1246
1246
1247
latest_monitor_update_id: u64,
1247
1248
@@ -3648,7 +3649,8 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
3648
3649
}
3649
3650
3650
3651
/// 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).
3652
3654
#[cfg(any(dual_funding, splicing))]
3653
3655
pub fn check_balance_meets_reserve_requirements(&self, channel_value: u64, balance: u64) -> Result<(), ChannelError> {
3654
3656
let holder_selected_channel_reserve_satoshis = get_v2_channel_reserve_satoshis(
@@ -4174,17 +4176,15 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
4174
4176
4175
4177
/// Get the splice_ack message that can be sent in response to splice initiation.
4176
4178
#[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 {
4180
4180
// Reuse the existing funding pubkey, in spite of the channel value changing
4181
4181
let funding_pubkey = self.get_holder_pubkeys().funding_pubkey;
4182
- Ok( msgs::SpliceAck {
4182
+ msgs::SpliceAck {
4183
4183
channel_id: self.channel_id,
4184
4184
funding_contribution_satoshis: our_funding_contribution_satoshis,
4185
4185
funding_pubkey,
4186
4186
require_confirmed_inputs: None,
4187
- })
4187
+ }
4188
4188
}
4189
4189
}
4190
4190
@@ -7908,23 +7908,118 @@ impl<SP: Deref> Channel<SP> where
7908
7908
}
7909
7909
}
7910
7910
7911
+ /// Initiate splicing
7911
7912
#[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
+ }
7921
7923
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
+ )));
7927
7989
}
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(())
7928
8023
}
7929
8024
7930
8025
// Send stuff to our remote peers:
0 commit comments