@@ -1181,12 +1181,13 @@ impl UnfundedChannelContext {
1181
1181
/// Info about a pending splice, used in the pre-splice channel
1182
1182
#[cfg(splicing)]
1183
1183
#[derive(Clone)]
1184
- pub(crate) struct PendingSpliceInfoPre {
1184
+ struct PendingSpliceInfoPre {
1185
1185
pub our_funding_contribution: i64,
1186
1186
}
1187
1187
1188
1188
#[cfg(splicing)]
1189
1189
impl PendingSpliceInfoPre {
1190
+ #[inline]
1190
1191
fn add_checked(base: u64, delta: i64) -> u64 {
1191
1192
if delta >= 0 {
1192
1193
base.saturating_add(delta as u64)
@@ -1238,7 +1239,7 @@ pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
1238
1239
1239
1240
/// Info about an in-progress, pending splice (if any), on the pre-splice channel
1240
1241
#[cfg(splicing)]
1241
- pub(crate) pending_splice_pre: Option<PendingSpliceInfoPre>,
1242
+ pending_splice_pre: Option<PendingSpliceInfoPre>,
1242
1243
1243
1244
latest_monitor_update_id: u64,
1244
1245
@@ -3419,7 +3420,8 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
3419
3420
}
3420
3421
3421
3422
/// Check that a balance value meets the channel reserve requirements or violates them (below reserve).
3422
- /// The channel value is an input, so that this can be used for checks with new planned channel value.
3423
+ /// The channel value is an input as opposed to using from self, so that this can be used in case of splicing
3424
+ /// to checks with new channel value (before being comitted to it).
3423
3425
#[cfg(any(dual_funding, splicing))]
3424
3426
pub fn check_balance_meets_reserve_requirements(&self, channel_value: u64, balance: u64) -> Result<(), ChannelError> {
3425
3427
let holder_selected_channel_reserve_satoshis = get_v2_channel_reserve_satoshis(
@@ -3848,17 +3850,15 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
3848
3850
3849
3851
/// Get the splice_ack message that can be sent in response to splice initiation.
3850
3852
#[cfg(splicing)]
3851
- pub fn get_splice_ack(&mut self, our_funding_contribution_satoshis: i64) -> Result<msgs::SpliceAck, ChannelError> {
3852
- // TODO(splicing): checks
3853
-
3853
+ pub fn get_splice_ack(&self, our_funding_contribution_satoshis: i64) -> msgs::SpliceAck {
3854
3854
// Reuse the existing funding pubkey, in spite of the channel value changing
3855
3855
let funding_pubkey = self.get_holder_pubkeys().funding_pubkey;
3856
- Ok( msgs::SpliceAck {
3856
+ msgs::SpliceAck {
3857
3857
channel_id: self.channel_id,
3858
3858
funding_contribution_satoshis: our_funding_contribution_satoshis,
3859
3859
funding_pubkey,
3860
3860
require_confirmed_inputs: None,
3861
- })
3861
+ }
3862
3862
}
3863
3863
}
3864
3864
@@ -7439,23 +7439,118 @@ impl<SP: Deref> Channel<SP> where
7439
7439
}
7440
7440
}
7441
7441
7442
+ /// Initiate splicing
7442
7443
#[cfg(splicing)]
7443
- pub fn splice_init<ES: Deref, L: Deref>(
7444
- &mut self, our_funding_contribution_satoshis: i64,
7445
- _signer_provider: &SP, _entropy_source: &ES, _holder_node_id: PublicKey, _logger: &L
7446
- )
7447
- -> Result<msgs::SpliceAck, ChannelError>
7448
- where ES::Target: EntropySource, L::Target: Logger
7449
- {
7450
- if !self.context.is_outbound() {
7451
- // TODO(splicing): Apply start of splice (splice_start)
7444
+ pub fn splice_channel(&mut self, our_funding_contribution_satoshis: i64,
7445
+ funding_feerate_perkw: u32, locktime: u32,
7446
+ ) -> Result<msgs::SpliceInit, ChannelError> {
7447
+ // Check if a splice has been initiated already.
7448
+ // Note: this could be handled more nicely, and support multiple outstanding splice's, the incoming splice_ack matters anyways.
7449
+ if let Some(splice_info) = &self.context.pending_splice_pre {
7450
+ return Err(ChannelError::Warn(format!(
7451
+ "Channel has already a splice pending, contribution {}", splice_info.our_funding_contribution
7452
+ )));
7453
+ }
7452
7454
7453
- let splice_ack_msg = self.context.get_splice_ack(our_funding_contribution_satoshis)?;
7454
- // TODO(splicing): start interactive funding negotiation
7455
- Ok(splice_ack_msg)
7456
- } else {
7457
- Err(ChannelError::Warn("Internal consistency error: splice_init on inbound channel".into()))
7455
+ if !matches!(self.context.channel_state, ChannelState::ChannelReady(_)) {
7456
+ return Err(ChannelError::Warn(format!("Cannot initiate splicing, as channel is not Ready")));
7457
+ }
7458
+
7459
+ let pre_channel_value = self.context.get_value_satoshis();
7460
+ // Sanity check: capacity cannot decrease below 0
7461
+ if (pre_channel_value as i64).saturating_add(our_funding_contribution_satoshis) < 0 {
7462
+ return Err(ChannelError::Warn(format!(
7463
+ "Post-splicing channel value cannot be negative. It was {} + {}",
7464
+ pre_channel_value, our_funding_contribution_satoshis
7465
+ )));
7466
+ }
7467
+
7468
+ if our_funding_contribution_satoshis < 0 {
7469
+ return Err(ChannelError::Warn(format!(
7470
+ "TODO(splicing): Splice-out not supported, only splice in, contribution {}",
7471
+ our_funding_contribution_satoshis,
7472
+ )));
7473
+ }
7474
+
7475
+ // Note: post-splice channel value is not yet known at this point, counterpary contribution is not known
7476
+ // (Cannot test for miminum required post-splice channel value)
7477
+
7478
+ self.context.pending_splice_pre = Some(PendingSpliceInfoPre {
7479
+ our_funding_contribution: our_funding_contribution_satoshis,
7480
+ });
7481
+
7482
+ let msg = self.context.get_splice_init(our_funding_contribution_satoshis, funding_feerate_perkw, locktime);
7483
+ Ok(msg)
7484
+ }
7485
+
7486
+ /// Handle splice_init
7487
+ #[cfg(splicing)]
7488
+ pub fn splice_init(
7489
+ &mut self, their_funding_contribution_satoshis: i64, our_funding_contribution_satoshis: i64,
7490
+ ) -> Result<msgs::SpliceAck, ChannelError> {
7491
+ // Check if a splice has been initiated already.
7492
+ // Note: this could be handled more nicely, and support multiple outstanding splice's, the incoming splice_ack matters anyways.
7493
+ if let Some(splice_info) = &self.context.pending_splice_pre {
7494
+ return Err(ChannelError::Warn(format!(
7495
+ "Channel has already a splice pending, contribution {}", splice_info.our_funding_contribution,
7496
+ )));
7497
+ }
7498
+
7499
+ if !matches!(self.context.channel_state, ChannelState::ChannelReady(_)) {
7500
+ return Err(ChannelError::Warn(format!("Splicing requested on a channel that is not Ready")));
7501
+ }
7502
+
7503
+ let pre_channel_value = self.context.get_value_satoshis();
7504
+ // Sanity check: capacity cannot decrease below 0
7505
+ if (pre_channel_value as i64)
7506
+ .saturating_add(their_funding_contribution_satoshis)
7507
+ .saturating_add(our_funding_contribution_satoshis) < 0
7508
+ {
7509
+ return Err(ChannelError::Warn(format!(
7510
+ "Post-splicing channel value cannot be negative. It was {} + {} + {}",
7511
+ pre_channel_value, their_funding_contribution_satoshis, our_funding_contribution_satoshis,
7512
+ )));
7513
+ }
7514
+
7515
+ if their_funding_contribution_satoshis.saturating_add(our_funding_contribution_satoshis) < 0 {
7516
+ return Err(ChannelError::Warn(format!(
7517
+ "Splice-out not supported, only splice in, relative {} + {}",
7518
+ their_funding_contribution_satoshis, our_funding_contribution_satoshis,
7519
+ )));
7458
7520
}
7521
+
7522
+ let post_channel_value = PendingSpliceInfoPre::compute_post_value(pre_channel_value, their_funding_contribution_satoshis, our_funding_contribution_satoshis);
7523
+
7524
+ // Early check for reserve requirement, assuming maximum balance of full channel value
7525
+ // This will also be checked later at tx_complete
7526
+ let _res = self.context.check_balance_meets_reserve_requirements(post_channel_value, post_channel_value)?;
7527
+
7528
+ // TODO(splicing): Apply start of splice (splice_start)
7529
+
7530
+ let splice_ack_msg = self.context.get_splice_ack(our_funding_contribution_satoshis);
7531
+ // TODO(splicing): start interactive funding negotiation
7532
+ Ok(splice_ack_msg)
7533
+ }
7534
+
7535
+ /// Handle splice_ack
7536
+ #[cfg(splicing)]
7537
+ pub fn splice_ack(
7538
+ &mut self, their_funding_contribution_satoshis: i64,
7539
+ ) -> Result<(), ChannelError> {
7540
+ // check if splice is pending
7541
+ let pending_splice = if let Some(pending_splice) = &self.context.pending_splice_pre {
7542
+ pending_splice
7543
+ } else {
7544
+ return Err(ChannelError::Warn(format!("Channel is not in pending splice")));
7545
+ };
7546
+
7547
+ let pre_channel_value = self.context.get_value_satoshis();
7548
+ let post_channel_value = PendingSpliceInfoPre::compute_post_value(pre_channel_value, pending_splice.our_funding_contribution, their_funding_contribution_satoshis);
7549
+
7550
+ // Early check for reserve requirement, assuming maximum balance of full channel value
7551
+ // This will also be checked later at tx_complete
7552
+ let _res = self.context.check_balance_meets_reserve_requirements(post_channel_value, post_channel_value)?;
7553
+ Ok(())
7459
7554
}
7460
7555
7461
7556
// Send stuff to our remote peers:
0 commit comments