@@ -11,7 +11,6 @@ use bitcoin::amount::Amount;
1111use bitcoin::constants::ChainHash;
1212use bitcoin::script::{Script, ScriptBuf, Builder, WScriptHash};
1313use bitcoin::transaction::{Transaction, TxIn, TxOut};
14- use bitcoin::sighash;
1514use bitcoin::sighash::EcdsaSighashType;
1615use bitcoin::consensus::encode;
1716use bitcoin::absolute::LockTime;
@@ -25,7 +24,7 @@ use bitcoin::hash_types::{Txid, BlockHash};
2524use bitcoin::secp256k1::constants::PUBLIC_KEY_SIZE;
2625use bitcoin::secp256k1::{PublicKey,SecretKey};
2726use bitcoin::secp256k1::{Secp256k1,ecdsa::Signature};
28- use bitcoin::secp256k1;
27+ use bitcoin::{ secp256k1, sighash} ;
2928
3029use crate::ln::types::ChannelId;
3130use crate::types::payment::{PaymentPreimage, PaymentHash};
@@ -1718,6 +1717,30 @@ impl FundingScope {
17181717 }
17191718}
17201719
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+
17211744/// Contains everything about the channel including state, and various flags.
17221745pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
17231746 config: LegacyChannelConfig,
@@ -1752,6 +1775,10 @@ pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
17521775
17531776 secp_ctx: Secp256k1<secp256k1::All>,
17541777
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+
17551782 latest_monitor_update_id: u64,
17561783
17571784 holder_signer: ChannelSignerType<SP>,
@@ -2816,6 +2843,9 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
28162843 next_funding_txid: None,
28172844
28182845 is_holder_quiescence_initiator: None,
2846+
2847+ #[cfg(splicing)]
2848+ pending_splice_pre: None,
28192849 };
28202850
28212851 Ok((funding, channel_context))
@@ -3048,6 +3078,9 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
30483078 next_funding_txid: None,
30493079
30503080 is_holder_quiescence_initiator: None,
3081+
3082+ #[cfg(splicing)]
3083+ pending_splice_pre: None,
30513084 };
30523085
30533086 Ok((funding, channel_context))
@@ -4204,6 +4237,33 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
42044237 }
42054238 }
42064239
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+
42074267 /// Get the commitment tx fee for the local's (i.e. our) next commitment transaction based on the
42084268 /// number of pending HTLCs that are on track to be in our next commitment tx.
42094269 ///
@@ -4680,6 +4740,38 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
46804740 self.counterparty_cur_commitment_point = Some(counterparty_cur_commitment_point_override);
46814741 self.get_initial_counterparty_commitment_signature(funding, logger)
46824742 }
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+ }
46834775}
46844776
46854777// Internal utility functions for channels
@@ -8427,6 +8519,124 @@ impl<SP: Deref> FundedChannel<SP> where
84278519 }
84288520 }
84298521
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+ }
84308640
84318641 // Send stuff to our remote peers:
84328642
@@ -10972,6 +11182,9 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, &'c Channel
1097211182 next_funding_txid: None,
1097311183
1097411184 is_holder_quiescence_initiator: None,
11185+
11186+ #[cfg(splicing)]
11187+ pending_splice_pre: None,
1097511188 },
1097611189 interactive_tx_signing_session: None,
1097711190 is_v2_established,
@@ -12790,4 +13003,69 @@ mod tests {
1279013003 320
1279113004 );
1279213005 }
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+ }
1279313071}
0 commit comments