@@ -11,7 +11,6 @@ use bitcoin::amount::Amount;
11
11
use bitcoin::constants::ChainHash;
12
12
use bitcoin::script::{Script, ScriptBuf, Builder, WScriptHash};
13
13
use bitcoin::transaction::{Transaction, TxIn, TxOut};
14
- use bitcoin::sighash;
15
14
use bitcoin::sighash::EcdsaSighashType;
16
15
use bitcoin::consensus::encode;
17
16
use bitcoin::absolute::LockTime;
@@ -25,7 +24,7 @@ use bitcoin::hash_types::{Txid, BlockHash};
25
24
use bitcoin::secp256k1::constants::PUBLIC_KEY_SIZE;
26
25
use bitcoin::secp256k1::{PublicKey,SecretKey};
27
26
use bitcoin::secp256k1::{Secp256k1,ecdsa::Signature};
28
- use bitcoin::secp256k1;
27
+ use bitcoin::{ secp256k1, sighash} ;
29
28
30
29
use crate::ln::types::ChannelId;
31
30
use crate::types::payment::{PaymentPreimage, PaymentHash};
@@ -1718,6 +1717,30 @@ impl FundingScope {
1718
1717
}
1719
1718
}
1720
1719
1720
+ /// Info about a pending splice, used in the pre-splice channel
1721
+ #[cfg(splicing)]
1722
+ #[derive(Clone)]
1723
+ struct PendingSpliceInfoPre {
1724
+ pub our_funding_contribution: i64,
1725
+ }
1726
+
1727
+ #[cfg(splicing)]
1728
+ impl PendingSpliceInfoPre {
1729
+ #[inline]
1730
+ fn add_checked(base: u64, delta: i64) -> u64 {
1731
+ if delta >= 0 {
1732
+ base.saturating_add(delta as u64)
1733
+ } else {
1734
+ base.saturating_sub(delta.abs() as u64)
1735
+ }
1736
+ }
1737
+
1738
+ /// Compute the post-splice channel value from the pre-splice values and the peer contributions
1739
+ pub fn compute_post_value(pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64) -> u64 {
1740
+ Self::add_checked(pre_channel_value, our_funding_contribution.saturating_add(their_funding_contribution))
1741
+ }
1742
+ }
1743
+
1721
1744
/// Contains everything about the channel including state, and various flags.
1722
1745
pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
1723
1746
config: LegacyChannelConfig,
@@ -1752,6 +1775,10 @@ pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
1752
1775
1753
1776
secp_ctx: Secp256k1<secp256k1::All>,
1754
1777
1778
+ /// Info about an in-progress, pending splice (if any), on the pre-splice channel
1779
+ #[cfg(splicing)]
1780
+ pending_splice_pre: Option<PendingSpliceInfoPre>,
1781
+
1755
1782
latest_monitor_update_id: u64,
1756
1783
1757
1784
holder_signer: ChannelSignerType<SP>,
@@ -2816,6 +2843,9 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
2816
2843
next_funding_txid: None,
2817
2844
2818
2845
is_holder_quiescence_initiator: None,
2846
+
2847
+ #[cfg(splicing)]
2848
+ pending_splice_pre: None,
2819
2849
};
2820
2850
2821
2851
Ok((funding, channel_context))
@@ -3048,6 +3078,9 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
3048
3078
next_funding_txid: None,
3049
3079
3050
3080
is_holder_quiescence_initiator: None,
3081
+
3082
+ #[cfg(splicing)]
3083
+ pending_splice_pre: None,
3051
3084
};
3052
3085
3053
3086
Ok((funding, channel_context))
@@ -4204,6 +4237,33 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
4204
4237
}
4205
4238
}
4206
4239
4240
+ /// Check that a balance value meets the channel reserve requirements or violates them (below reserve).
4241
+ /// The channel value is an input as opposed to using from self, so that this can be used in case of splicing
4242
+ /// to checks with new channel value (before being comitted to it).
4243
+ #[cfg(splicing)]
4244
+ pub fn check_balance_meets_reserve_requirements(&self, balance: u64, channel_value: u64) -> Result<(), ChannelError> {
4245
+ if balance == 0 {
4246
+ return Ok(());
4247
+ }
4248
+ let holder_selected_channel_reserve_satoshis = get_v2_channel_reserve_satoshis(
4249
+ channel_value, self.holder_dust_limit_satoshis);
4250
+ if balance < holder_selected_channel_reserve_satoshis {
4251
+ return Err(ChannelError::Warn(format!(
4252
+ "Balance below reserve mandated by holder, {} vs {}",
4253
+ balance, holder_selected_channel_reserve_satoshis,
4254
+ )));
4255
+ }
4256
+ let counterparty_selected_channel_reserve_satoshis = get_v2_channel_reserve_satoshis(
4257
+ channel_value, self.counterparty_dust_limit_satoshis);
4258
+ if balance < counterparty_selected_channel_reserve_satoshis {
4259
+ return Err(ChannelError::Warn(format!(
4260
+ "Balance below reserve mandated by counterparty, {} vs {}",
4261
+ balance, counterparty_selected_channel_reserve_satoshis,
4262
+ )));
4263
+ }
4264
+ Ok(())
4265
+ }
4266
+
4207
4267
/// Get the commitment tx fee for the local's (i.e. our) next commitment transaction based on the
4208
4268
/// number of pending HTLCs that are on track to be in our next commitment tx.
4209
4269
///
@@ -4680,6 +4740,38 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
4680
4740
self.counterparty_cur_commitment_point = Some(counterparty_cur_commitment_point_override);
4681
4741
self.get_initial_counterparty_commitment_signature(funding, logger)
4682
4742
}
4743
+
4744
+ /// Get the splice message that can be sent during splice initiation.
4745
+ #[cfg(splicing)]
4746
+ pub fn get_splice_init(&self, our_funding_contribution_satoshis: i64,
4747
+ funding_feerate_perkw: u32, locktime: u32,
4748
+ ) -> msgs::SpliceInit {
4749
+ // Reuse the existing funding pubkey, in spite of the channel value changing
4750
+ // (though at this point we don't know the new value yet, due tue the optional counterparty contribution)
4751
+ // Note that channel_keys_id is supposed NOT to change
4752
+ let funding_pubkey = self.get_holder_pubkeys().funding_pubkey.clone();
4753
+ msgs::SpliceInit {
4754
+ channel_id: self.channel_id,
4755
+ funding_contribution_satoshis: our_funding_contribution_satoshis,
4756
+ funding_feerate_perkw,
4757
+ locktime,
4758
+ funding_pubkey,
4759
+ require_confirmed_inputs: None,
4760
+ }
4761
+ }
4762
+
4763
+ /// Get the splice_ack message that can be sent in response to splice initiation.
4764
+ #[cfg(splicing)]
4765
+ pub fn get_splice_ack(&self, our_funding_contribution_satoshis: i64) -> msgs::SpliceAck {
4766
+ // Reuse the existing funding pubkey, in spite of the channel value changing
4767
+ let funding_pubkey = self.get_holder_pubkeys().funding_pubkey;
4768
+ msgs::SpliceAck {
4769
+ channel_id: self.channel_id,
4770
+ funding_contribution_satoshis: our_funding_contribution_satoshis,
4771
+ funding_pubkey,
4772
+ require_confirmed_inputs: None,
4773
+ }
4774
+ }
4683
4775
}
4684
4776
4685
4777
// Internal utility functions for channels
@@ -8427,6 +8519,124 @@ impl<SP: Deref> FundedChannel<SP> where
8427
8519
}
8428
8520
}
8429
8521
8522
+ /// Initiate splicing
8523
+ #[cfg(splicing)]
8524
+ pub fn splice_channel(&mut self, our_funding_contribution_satoshis: i64,
8525
+ funding_feerate_perkw: u32, locktime: u32,
8526
+ ) -> Result<msgs::SpliceInit, ChannelError> {
8527
+ // Check if a splice has been initiated already.
8528
+ // Note: this could be handled more nicely, and support multiple outstanding splice's, the incoming splice_ack matters anyways.
8529
+ if let Some(splice_info) = &self.context.pending_splice_pre {
8530
+ return Err(ChannelError::Warn(format!(
8531
+ "Channel has already a splice pending, contribution {}", splice_info.our_funding_contribution
8532
+ )));
8533
+ }
8534
+
8535
+ if !matches!(self.context.channel_state, ChannelState::ChannelReady(_)) {
8536
+ return Err(ChannelError::Warn(format!("Cannot initiate splicing, as channel is not Ready")));
8537
+ }
8538
+
8539
+ let pre_channel_value = self.funding.get_value_satoshis();
8540
+ // Sanity check: capacity cannot decrease below 0
8541
+ if (pre_channel_value as i64).saturating_add(our_funding_contribution_satoshis) < 0 {
8542
+ return Err(ChannelError::Warn(format!(
8543
+ "Post-splicing channel value cannot be negative. It was {} + {}",
8544
+ pre_channel_value, our_funding_contribution_satoshis
8545
+ )));
8546
+ }
8547
+
8548
+ if our_funding_contribution_satoshis < 0 {
8549
+ return Err(ChannelError::Warn(format!(
8550
+ "TODO(splicing): Splice-out not supported, only splice in, contribution {}",
8551
+ our_funding_contribution_satoshis,
8552
+ )));
8553
+ }
8554
+
8555
+ // Note: post-splice channel value is not yet known at this point, counterpary contribution is not known
8556
+ // (Cannot test for miminum required post-splice channel value)
8557
+
8558
+ self.context.pending_splice_pre = Some(PendingSpliceInfoPre {
8559
+ our_funding_contribution: our_funding_contribution_satoshis,
8560
+ });
8561
+
8562
+ let msg = self.context.get_splice_init(our_funding_contribution_satoshis, funding_feerate_perkw, locktime);
8563
+ Ok(msg)
8564
+ }
8565
+
8566
+ /// Handle splice_init
8567
+ #[cfg(splicing)]
8568
+ pub fn splice_init(&mut self, msg: &msgs::SpliceInit) -> Result<msgs::SpliceAck, ChannelError> {
8569
+ let their_funding_contribution_satoshis = msg.funding_contribution_satoshis;
8570
+ // TODO(splicing): Currently not possible to contribute on the splicing-acceptor side
8571
+ let our_funding_contribution_satoshis = 0i64;
8572
+
8573
+ // Check if a splice has been initiated already.
8574
+ // Note: this could be handled more nicely, and support multiple outstanding splice's, the incoming splice_ack matters anyways.
8575
+ if let Some(splice_info) = &self.context.pending_splice_pre {
8576
+ return Err(ChannelError::Warn(format!(
8577
+ "Channel has already a splice pending, contribution {}", splice_info.our_funding_contribution,
8578
+ )));
8579
+ }
8580
+
8581
+ if !matches!(self.context.channel_state, ChannelState::ChannelReady(_)) {
8582
+ return Err(ChannelError::Warn(format!("Splicing requested on a channel that is not Ready")));
8583
+ }
8584
+
8585
+ let pre_channel_value = self.funding.get_value_satoshis();
8586
+ // Sanity check: capacity cannot decrease below 0
8587
+ if (pre_channel_value as i64)
8588
+ .saturating_add(their_funding_contribution_satoshis)
8589
+ .saturating_add(our_funding_contribution_satoshis) < 0
8590
+ {
8591
+ return Err(ChannelError::Warn(format!(
8592
+ "Post-splicing channel value cannot be negative. It was {} + {} + {}",
8593
+ pre_channel_value, their_funding_contribution_satoshis, our_funding_contribution_satoshis,
8594
+ )));
8595
+ }
8596
+
8597
+ if their_funding_contribution_satoshis.saturating_add(our_funding_contribution_satoshis) < 0 {
8598
+ return Err(ChannelError::Warn(format!(
8599
+ "Splice-out not supported, only splice in, relative {} + {}",
8600
+ their_funding_contribution_satoshis, our_funding_contribution_satoshis,
8601
+ )));
8602
+ }
8603
+
8604
+ let post_channel_value = PendingSpliceInfoPre::compute_post_value(pre_channel_value, their_funding_contribution_satoshis, our_funding_contribution_satoshis);
8605
+ let post_balance = PendingSpliceInfoPre::add_checked(self.funding.value_to_self_msat, our_funding_contribution_satoshis);
8606
+ // Early check for reserve requirement, assuming maximum balance of full channel value
8607
+ // This will also be checked later at tx_complete
8608
+ let _res = self.context.check_balance_meets_reserve_requirements(post_balance, post_channel_value)?;
8609
+
8610
+ // TODO(splicing): Store msg.funding_pubkey
8611
+ // TODO(splicing): Apply start of splice (splice_start)
8612
+
8613
+ let splice_ack_msg = self.context.get_splice_ack(our_funding_contribution_satoshis);
8614
+ // TODO(splicing): start interactive funding negotiation
8615
+ Ok(splice_ack_msg)
8616
+ }
8617
+
8618
+ /// Handle splice_ack
8619
+ #[cfg(splicing)]
8620
+ pub fn splice_ack(&mut self, msg: &msgs::SpliceAck) -> Result<(), ChannelError> {
8621
+ let their_funding_contribution_satoshis = msg.funding_contribution_satoshis;
8622
+
8623
+ // check if splice is pending
8624
+ let pending_splice = if let Some(pending_splice) = &self.context.pending_splice_pre {
8625
+ pending_splice
8626
+ } else {
8627
+ return Err(ChannelError::Warn(format!("Channel is not in pending splice")));
8628
+ };
8629
+
8630
+ let our_funding_contribution = pending_splice.our_funding_contribution;
8631
+
8632
+ let pre_channel_value = self.funding.get_value_satoshis();
8633
+ let post_channel_value = PendingSpliceInfoPre::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution_satoshis);
8634
+ let post_balance = PendingSpliceInfoPre::add_checked(self.funding.value_to_self_msat, our_funding_contribution);
8635
+ // Early check for reserve requirement, assuming maximum balance of full channel value
8636
+ // This will also be checked later at tx_complete
8637
+ let _res = self.context.check_balance_meets_reserve_requirements(post_balance, post_channel_value)?;
8638
+ Ok(())
8639
+ }
8430
8640
8431
8641
// Send stuff to our remote peers:
8432
8642
@@ -10972,6 +11182,9 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, &'c Channel
10972
11182
next_funding_txid: None,
10973
11183
10974
11184
is_holder_quiescence_initiator: None,
11185
+
11186
+ #[cfg(splicing)]
11187
+ pending_splice_pre: None,
10975
11188
},
10976
11189
interactive_tx_signing_session: None,
10977
11190
is_v2_established,
@@ -12790,4 +13003,69 @@ mod tests {
12790
13003
320
12791
13004
);
12792
13005
}
13006
+
13007
+ #[cfg(all(test, splicing))]
13008
+ fn get_pre_and_post(pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64) -> (u64, u64) {
13009
+ use crate::ln::channel::PendingSpliceInfoPre;
13010
+
13011
+ let post_channel_value = PendingSpliceInfoPre::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution);
13012
+ (pre_channel_value, post_channel_value)
13013
+ }
13014
+
13015
+ #[cfg(all(test, splicing))]
13016
+ #[test]
13017
+ fn test_splice_compute_post_value() {
13018
+ {
13019
+ // increase, small amounts
13020
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 6_000, 0);
13021
+ assert_eq!(pre_channel_value, 9_000);
13022
+ assert_eq!(post_channel_value, 15_000);
13023
+ }
13024
+ {
13025
+ // increase, small amounts
13026
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 4_000, 2_000);
13027
+ assert_eq!(pre_channel_value, 9_000);
13028
+ assert_eq!(post_channel_value, 15_000);
13029
+ }
13030
+ {
13031
+ // increase, small amounts
13032
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 0, 6_000);
13033
+ assert_eq!(pre_channel_value, 9_000);
13034
+ assert_eq!(post_channel_value, 15_000);
13035
+ }
13036
+ {
13037
+ // decrease, small amounts
13038
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -6_000, 0);
13039
+ assert_eq!(pre_channel_value, 15_000);
13040
+ assert_eq!(post_channel_value, 9_000);
13041
+ }
13042
+ {
13043
+ // decrease, small amounts
13044
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -4_000, -2_000);
13045
+ assert_eq!(pre_channel_value, 15_000);
13046
+ assert_eq!(post_channel_value, 9_000);
13047
+ }
13048
+ {
13049
+ // increase and decrease
13050
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, 4_000, -2_000);
13051
+ assert_eq!(pre_channel_value, 15_000);
13052
+ assert_eq!(post_channel_value, 17_000);
13053
+ }
13054
+ let base2: u64 = 2;
13055
+ let huge63i3 = (base2.pow(63) - 3) as i64;
13056
+ assert_eq!(huge63i3, 9223372036854775805);
13057
+ assert_eq!(-huge63i3, -9223372036854775805);
13058
+ {
13059
+ // increase, large amount
13060
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, 3);
13061
+ assert_eq!(pre_channel_value, 9_000);
13062
+ assert_eq!(post_channel_value, 9223372036854784807);
13063
+ }
13064
+ {
13065
+ // increase, large amounts
13066
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, huge63i3);
13067
+ assert_eq!(pre_channel_value, 9_000);
13068
+ assert_eq!(post_channel_value, 9223372036854784807);
13069
+ }
13070
+ }
12793
13071
}
0 commit comments