@@ -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};
@@ -1183,6 +1182,30 @@ impl UnfundedChannelContext {
11831182 }
11841183}
11851184
1185+ /// Info about a pending splice, used in the pre-splice channel
1186+ #[cfg(splicing)]
1187+ #[derive(Clone)]
1188+ struct PendingSpliceInfoPre {
1189+ pub our_funding_contribution: i64,
1190+ }
1191+
1192+ #[cfg(splicing)]
1193+ impl PendingSpliceInfoPre {
1194+ #[inline]
1195+ fn add_checked(base: u64, delta: i64) -> u64 {
1196+ if delta >= 0 {
1197+ base.saturating_add(delta as u64)
1198+ } else {
1199+ base.saturating_sub(delta.abs() as u64)
1200+ }
1201+ }
1202+
1203+ /// Compute the post-splice channel value from the pre-splice values and the peer contributions
1204+ pub fn compute_post_value(pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64) -> u64 {
1205+ Self::add_checked(pre_channel_value, our_funding_contribution.saturating_add(their_funding_contribution))
1206+ }
1207+ }
1208+
11861209/// Contains everything about the channel including state, and various flags.
11871210pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
11881211 config: LegacyChannelConfig,
@@ -1218,6 +1241,10 @@ pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
12181241 secp_ctx: Secp256k1<secp256k1::All>,
12191242 channel_value_satoshis: u64,
12201243
1244+ /// Info about an in-progress, pending splice (if any), on the pre-splice channel
1245+ #[cfg(splicing)]
1246+ pending_splice_pre: Option<PendingSpliceInfoPre>,
1247+
12211248 latest_monitor_update_id: u64,
12221249
12231250 holder_signer: ChannelSignerType<SP>,
@@ -2328,6 +2355,9 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
23282355 is_manual_broadcast: false,
23292356
23302357 next_funding_txid: None,
2358+
2359+ #[cfg(splicing)]
2360+ pending_splice_pre: None,
23312361 };
23322362
23332363 Ok(channel_context)
@@ -2561,6 +2591,9 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
25612591 local_initiated_shutdown: None,
25622592 is_manual_broadcast: false,
25632593 next_funding_txid: None,
2594+
2595+ #[cfg(splicing)]
2596+ pending_splice_pre: None,
25642597 })
25652598 }
25662599
@@ -3744,6 +3777,33 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
37443777 (context.holder_selected_channel_reserve_satoshis, context.counterparty_selected_channel_reserve_satoshis)
37453778 }
37463779
3780+ /// Check that a balance value meets the channel reserve requirements or violates them (below reserve).
3781+ /// The channel value is an input as opposed to using from self, so that this can be used in case of splicing
3782+ /// to checks with new channel value (before being comitted to it).
3783+ #[cfg(splicing)]
3784+ pub fn check_balance_meets_reserve_requirements(&self, balance: u64, channel_value: u64) -> Result<(), ChannelError> {
3785+ if balance == 0 {
3786+ return Ok(());
3787+ }
3788+ let holder_selected_channel_reserve_satoshis = get_v2_channel_reserve_satoshis(
3789+ channel_value, self.holder_dust_limit_satoshis);
3790+ if balance < holder_selected_channel_reserve_satoshis {
3791+ return Err(ChannelError::Warn(format!(
3792+ "Balance below reserve mandated by holder, {} vs {}",
3793+ balance, holder_selected_channel_reserve_satoshis,
3794+ )));
3795+ }
3796+ let counterparty_selected_channel_reserve_satoshis = get_v2_channel_reserve_satoshis(
3797+ channel_value, self.counterparty_dust_limit_satoshis);
3798+ if balance < counterparty_selected_channel_reserve_satoshis {
3799+ return Err(ChannelError::Warn(format!(
3800+ "Balance below reserve mandated by counterparty, {} vs {}",
3801+ balance, counterparty_selected_channel_reserve_satoshis,
3802+ )));
3803+ }
3804+ Ok(())
3805+ }
3806+
37473807 /// Get the commitment tx fee for the local's (i.e. our) next commitment transaction based on the
37483808 /// number of pending HTLCs that are on track to be in our next commitment tx.
37493809 ///
@@ -4214,6 +4274,38 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
42144274 self.channel_transaction_parameters = channel_transaction_parameters;
42154275 self.get_initial_counterparty_commitment_signature(logger)
42164276 }
4277+
4278+ /// Get the splice message that can be sent during splice initiation.
4279+ #[cfg(splicing)]
4280+ pub fn get_splice_init(&self, our_funding_contribution_satoshis: i64,
4281+ funding_feerate_perkw: u32, locktime: u32,
4282+ ) -> msgs::SpliceInit {
4283+ // Reuse the existing funding pubkey, in spite of the channel value changing
4284+ // (though at this point we don't know the new value yet, due tue the optional counterparty contribution)
4285+ // Note that channel_keys_id is supposed NOT to change
4286+ let funding_pubkey = self.get_holder_pubkeys().funding_pubkey.clone();
4287+ msgs::SpliceInit {
4288+ channel_id: self.channel_id,
4289+ funding_contribution_satoshis: our_funding_contribution_satoshis,
4290+ funding_feerate_perkw,
4291+ locktime,
4292+ funding_pubkey,
4293+ require_confirmed_inputs: None,
4294+ }
4295+ }
4296+
4297+ /// Get the splice_ack message that can be sent in response to splice initiation.
4298+ #[cfg(splicing)]
4299+ pub fn get_splice_ack(&self, our_funding_contribution_satoshis: i64) -> msgs::SpliceAck {
4300+ // Reuse the existing funding pubkey, in spite of the channel value changing
4301+ let funding_pubkey = self.get_holder_pubkeys().funding_pubkey;
4302+ msgs::SpliceAck {
4303+ channel_id: self.channel_id,
4304+ funding_contribution_satoshis: our_funding_contribution_satoshis,
4305+ funding_pubkey,
4306+ require_confirmed_inputs: None,
4307+ }
4308+ }
42174309}
42184310
42194311// Internal utility functions for channels
@@ -7941,6 +8033,124 @@ impl<SP: Deref> Channel<SP> where
79418033 }
79428034 }
79438035
8036+ /// Initiate splicing
8037+ #[cfg(splicing)]
8038+ pub fn splice_channel(&mut self, our_funding_contribution_satoshis: i64,
8039+ funding_feerate_perkw: u32, locktime: u32,
8040+ ) -> Result<msgs::SpliceInit, ChannelError> {
8041+ // Check if a splice has been initiated already.
8042+ // Note: this could be handled more nicely, and support multiple outstanding splice's, the incoming splice_ack matters anyways.
8043+ if let Some(splice_info) = &self.context.pending_splice_pre {
8044+ return Err(ChannelError::Warn(format!(
8045+ "Channel has already a splice pending, contribution {}", splice_info.our_funding_contribution
8046+ )));
8047+ }
8048+
8049+ if !matches!(self.context.channel_state, ChannelState::ChannelReady(_)) {
8050+ return Err(ChannelError::Warn(format!("Cannot initiate splicing, as channel is not Ready")));
8051+ }
8052+
8053+ let pre_channel_value = self.context.get_value_satoshis();
8054+ // Sanity check: capacity cannot decrease below 0
8055+ if (pre_channel_value as i64).saturating_add(our_funding_contribution_satoshis) < 0 {
8056+ return Err(ChannelError::Warn(format!(
8057+ "Post-splicing channel value cannot be negative. It was {} + {}",
8058+ pre_channel_value, our_funding_contribution_satoshis
8059+ )));
8060+ }
8061+
8062+ if our_funding_contribution_satoshis < 0 {
8063+ return Err(ChannelError::Warn(format!(
8064+ "TODO(splicing): Splice-out not supported, only splice in, contribution {}",
8065+ our_funding_contribution_satoshis,
8066+ )));
8067+ }
8068+
8069+ // Note: post-splice channel value is not yet known at this point, counterpary contribution is not known
8070+ // (Cannot test for miminum required post-splice channel value)
8071+
8072+ self.context.pending_splice_pre = Some(PendingSpliceInfoPre {
8073+ our_funding_contribution: our_funding_contribution_satoshis,
8074+ });
8075+
8076+ let msg = self.context.get_splice_init(our_funding_contribution_satoshis, funding_feerate_perkw, locktime);
8077+ Ok(msg)
8078+ }
8079+
8080+ /// Handle splice_init
8081+ #[cfg(splicing)]
8082+ pub fn splice_init(&mut self, msg: &msgs::SpliceInit) -> Result<msgs::SpliceAck, ChannelError> {
8083+ let their_funding_contribution_satoshis = msg.funding_contribution_satoshis;
8084+ // TODO(splicing): Currently not possible to contribute on the splicing-acceptor side
8085+ let our_funding_contribution_satoshis = 0i64;
8086+
8087+ // Check if a splice has been initiated already.
8088+ // Note: this could be handled more nicely, and support multiple outstanding splice's, the incoming splice_ack matters anyways.
8089+ if let Some(splice_info) = &self.context.pending_splice_pre {
8090+ return Err(ChannelError::Warn(format!(
8091+ "Channel has already a splice pending, contribution {}", splice_info.our_funding_contribution,
8092+ )));
8093+ }
8094+
8095+ if !matches!(self.context.channel_state, ChannelState::ChannelReady(_)) {
8096+ return Err(ChannelError::Warn(format!("Splicing requested on a channel that is not Ready")));
8097+ }
8098+
8099+ let pre_channel_value = self.context.get_value_satoshis();
8100+ // Sanity check: capacity cannot decrease below 0
8101+ if (pre_channel_value as i64)
8102+ .saturating_add(their_funding_contribution_satoshis)
8103+ .saturating_add(our_funding_contribution_satoshis) < 0
8104+ {
8105+ return Err(ChannelError::Warn(format!(
8106+ "Post-splicing channel value cannot be negative. It was {} + {} + {}",
8107+ pre_channel_value, their_funding_contribution_satoshis, our_funding_contribution_satoshis,
8108+ )));
8109+ }
8110+
8111+ if their_funding_contribution_satoshis.saturating_add(our_funding_contribution_satoshis) < 0 {
8112+ return Err(ChannelError::Warn(format!(
8113+ "Splice-out not supported, only splice in, relative {} + {}",
8114+ their_funding_contribution_satoshis, our_funding_contribution_satoshis,
8115+ )));
8116+ }
8117+
8118+ let post_channel_value = PendingSpliceInfoPre::compute_post_value(pre_channel_value, their_funding_contribution_satoshis, our_funding_contribution_satoshis);
8119+ let post_balance = PendingSpliceInfoPre::add_checked(self.context.value_to_self_msat, our_funding_contribution_satoshis);
8120+ // Early check for reserve requirement, assuming maximum balance of full channel value
8121+ // This will also be checked later at tx_complete
8122+ let _res = self.context.check_balance_meets_reserve_requirements(post_balance, post_channel_value)?;
8123+
8124+ // TODO(splicing): Store msg.funding_pubkey
8125+ // TODO(splicing): Apply start of splice (splice_start)
8126+
8127+ let splice_ack_msg = self.context.get_splice_ack(our_funding_contribution_satoshis);
8128+ // TODO(splicing): start interactive funding negotiation
8129+ Ok(splice_ack_msg)
8130+ }
8131+
8132+ /// Handle splice_ack
8133+ #[cfg(splicing)]
8134+ pub fn splice_ack(&mut self, msg: &msgs::SpliceAck) -> Result<(), ChannelError> {
8135+ let their_funding_contribution_satoshis = msg.funding_contribution_satoshis;
8136+
8137+ // check if splice is pending
8138+ let pending_splice = if let Some(pending_splice) = &self.context.pending_splice_pre {
8139+ pending_splice
8140+ } else {
8141+ return Err(ChannelError::Warn(format!("Channel is not in pending splice")));
8142+ };
8143+
8144+ let our_funding_contribution = pending_splice.our_funding_contribution;
8145+
8146+ let pre_channel_value = self.context.get_value_satoshis();
8147+ let post_channel_value = PendingSpliceInfoPre::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution_satoshis);
8148+ let post_balance = PendingSpliceInfoPre::add_checked(self.context.value_to_self_msat, our_funding_contribution);
8149+ // Early check for reserve requirement, assuming maximum balance of full channel value
8150+ // This will also be checked later at tx_complete
8151+ let _res = self.context.check_balance_meets_reserve_requirements(post_balance, post_channel_value)?;
8152+ Ok(())
8153+ }
79448154
79458155 // Send stuff to our remote peers:
79468156
@@ -10259,6 +10469,9 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
1025910469 // during a signing session, but have not received `tx_signatures` we MUST set `next_funding_txid`
1026010470 // to the txid of that interactive transaction, else we MUST NOT set it.
1026110471 next_funding_txid: None,
10472+
10473+ #[cfg(splicing)]
10474+ pending_splice_pre: None,
1026210475 },
1026310476 interactive_tx_signing_session: None,
1026410477 })
@@ -12043,4 +12256,69 @@ mod tests {
1204312256 assert_eq!(node_a_chan.context.channel_state, ChannelState::AwaitingChannelReady(AwaitingChannelReadyFlags::THEIR_CHANNEL_READY));
1204412257 assert!(node_a_chan.check_get_channel_ready(0, &&logger).is_some());
1204512258 }
12259+
12260+ #[cfg(all(test, splicing))]
12261+ fn get_pre_and_post(pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64) -> (u64, u64) {
12262+ use crate::ln::channel::PendingSpliceInfoPre;
12263+
12264+ let post_channel_value = PendingSpliceInfoPre::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution);
12265+ (pre_channel_value, post_channel_value)
12266+ }
12267+
12268+ #[cfg(all(test, splicing))]
12269+ #[test]
12270+ fn test_splice_compute_post_value() {
12271+ {
12272+ // increase, small amounts
12273+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 6_000, 0);
12274+ assert_eq!(pre_channel_value, 9_000);
12275+ assert_eq!(post_channel_value, 15_000);
12276+ }
12277+ {
12278+ // increase, small amounts
12279+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 4_000, 2_000);
12280+ assert_eq!(pre_channel_value, 9_000);
12281+ assert_eq!(post_channel_value, 15_000);
12282+ }
12283+ {
12284+ // increase, small amounts
12285+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 0, 6_000);
12286+ assert_eq!(pre_channel_value, 9_000);
12287+ assert_eq!(post_channel_value, 15_000);
12288+ }
12289+ {
12290+ // decrease, small amounts
12291+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -6_000, 0);
12292+ assert_eq!(pre_channel_value, 15_000);
12293+ assert_eq!(post_channel_value, 9_000);
12294+ }
12295+ {
12296+ // decrease, small amounts
12297+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -4_000, -2_000);
12298+ assert_eq!(pre_channel_value, 15_000);
12299+ assert_eq!(post_channel_value, 9_000);
12300+ }
12301+ {
12302+ // increase and decrease
12303+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, 4_000, -2_000);
12304+ assert_eq!(pre_channel_value, 15_000);
12305+ assert_eq!(post_channel_value, 17_000);
12306+ }
12307+ let base2: u64 = 2;
12308+ let huge63i3 = (base2.pow(63) - 3) as i64;
12309+ assert_eq!(huge63i3, 9223372036854775805);
12310+ assert_eq!(-huge63i3, -9223372036854775805);
12311+ {
12312+ // increase, large amount
12313+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, 3);
12314+ assert_eq!(pre_channel_value, 9_000);
12315+ assert_eq!(post_channel_value, 9223372036854784807);
12316+ }
12317+ {
12318+ // increase, large amounts
12319+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, huge63i3);
12320+ assert_eq!(pre_channel_value, 9_000);
12321+ assert_eq!(post_channel_value, 9223372036854784807);
12322+ }
12323+ }
1204612324}
0 commit comments