@@ -11,7 +11,6 @@ use bitcoin::amount::Amount;
1111use bitcoin::constants::ChainHash;
1212use bitcoin::script::{Script, ScriptBuf, Builder, WScriptHash};
1313use bitcoin::transaction::{Transaction, TxIn};
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};
@@ -1182,6 +1181,30 @@ impl UnfundedChannelContext {
11821181 }
11831182}
11841183
1184+ /// Info about a pending splice, used in the pre-splice channel
1185+ #[cfg(splicing)]
1186+ #[derive(Clone)]
1187+ struct PendingSpliceInfoPre {
1188+ pub our_funding_contribution: i64,
1189+ }
1190+
1191+ #[cfg(splicing)]
1192+ impl PendingSpliceInfoPre {
1193+ #[inline]
1194+ fn add_checked(base: u64, delta: i64) -> u64 {
1195+ if delta >= 0 {
1196+ base.saturating_add(delta as u64)
1197+ } else {
1198+ base.saturating_sub(delta.abs() as u64)
1199+ }
1200+ }
1201+
1202+ /// Compute the post-splice channel value from the pre-splice values and the peer contributions
1203+ pub fn compute_post_value(pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64) -> u64 {
1204+ Self::add_checked(pre_channel_value, our_funding_contribution.saturating_add(their_funding_contribution))
1205+ }
1206+ }
1207+
11851208/// Contains everything about the channel including state, and various flags.
11861209pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
11871210 config: LegacyChannelConfig,
@@ -1217,6 +1240,10 @@ pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
12171240 secp_ctx: Secp256k1<secp256k1::All>,
12181241 channel_value_satoshis: u64,
12191242
1243+ /// Info about an in-progress, pending splice (if any), on the pre-splice channel
1244+ #[cfg(splicing)]
1245+ pending_splice_pre: Option<PendingSpliceInfoPre>,
1246+
12201247 latest_monitor_update_id: u64,
12211248
12221249 holder_signer: ChannelSignerType<SP>,
@@ -2207,6 +2234,9 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
22072234 is_manual_broadcast: false,
22082235
22092236 next_funding_txid: None,
2237+
2238+ #[cfg(splicing)]
2239+ pending_splice_pre: None,
22102240 };
22112241
22122242 Ok(channel_context)
@@ -2440,6 +2470,9 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
24402470 local_initiated_shutdown: None,
24412471 is_manual_broadcast: false,
24422472 next_funding_txid: None,
2473+
2474+ #[cfg(splicing)]
2475+ pending_splice_pre: None,
24432476 })
24442477 }
24452478
@@ -3615,6 +3648,33 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
36153648 (context.holder_selected_channel_reserve_satoshis, context.counterparty_selected_channel_reserve_satoshis)
36163649 }
36173650
3651+ /// Check that a balance value meets the channel reserve requirements or violates them (below reserve).
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).
3654+ #[cfg(splicing)]
3655+ pub fn check_balance_meets_reserve_requirements(&self, balance: u64, channel_value: u64) -> Result<(), ChannelError> {
3656+ if balance == 0 {
3657+ return Ok(());
3658+ }
3659+ let holder_selected_channel_reserve_satoshis = get_v2_channel_reserve_satoshis(
3660+ channel_value, self.holder_dust_limit_satoshis);
3661+ if balance < holder_selected_channel_reserve_satoshis {
3662+ return Err(ChannelError::Warn(format!(
3663+ "Balance below reserve mandated by holder, {} vs {}",
3664+ balance, holder_selected_channel_reserve_satoshis,
3665+ )));
3666+ }
3667+ let counterparty_selected_channel_reserve_satoshis = get_v2_channel_reserve_satoshis(
3668+ channel_value, self.counterparty_dust_limit_satoshis);
3669+ if balance < counterparty_selected_channel_reserve_satoshis {
3670+ return Err(ChannelError::Warn(format!(
3671+ "Balance below reserve mandated by counterparty, {} vs {}",
3672+ balance, counterparty_selected_channel_reserve_satoshis,
3673+ )));
3674+ }
3675+ Ok(())
3676+ }
3677+
36183678 /// Get the commitment tx fee for the local's (i.e. our) next commitment transaction based on the
36193679 /// number of pending HTLCs that are on track to be in our next commitment tx.
36203680 ///
@@ -4085,6 +4145,38 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
40854145 self.channel_transaction_parameters = channel_transaction_parameters;
40864146 self.get_initial_counterparty_commitment_signature(logger)
40874147 }
4148+
4149+ /// Get the splice message that can be sent during splice initiation.
4150+ #[cfg(splicing)]
4151+ pub fn get_splice_init(&self, our_funding_contribution_satoshis: i64,
4152+ funding_feerate_perkw: u32, locktime: u32,
4153+ ) -> msgs::SpliceInit {
4154+ // Reuse the existing funding pubkey, in spite of the channel value changing
4155+ // (though at this point we don't know the new value yet, due tue the optional counterparty contribution)
4156+ // Note that channel_keys_id is supposed NOT to change
4157+ let funding_pubkey = self.get_holder_pubkeys().funding_pubkey.clone();
4158+ msgs::SpliceInit {
4159+ channel_id: self.channel_id,
4160+ funding_contribution_satoshis: our_funding_contribution_satoshis,
4161+ funding_feerate_perkw,
4162+ locktime,
4163+ funding_pubkey,
4164+ require_confirmed_inputs: None,
4165+ }
4166+ }
4167+
4168+ /// Get the splice_ack message that can be sent in response to splice initiation.
4169+ #[cfg(splicing)]
4170+ pub fn get_splice_ack(&self, our_funding_contribution_satoshis: i64) -> msgs::SpliceAck {
4171+ // Reuse the existing funding pubkey, in spite of the channel value changing
4172+ let funding_pubkey = self.get_holder_pubkeys().funding_pubkey;
4173+ msgs::SpliceAck {
4174+ channel_id: self.channel_id,
4175+ funding_contribution_satoshis: our_funding_contribution_satoshis,
4176+ funding_pubkey,
4177+ require_confirmed_inputs: None,
4178+ }
4179+ }
40884180}
40894181
40904182// Internal utility functions for channels
@@ -7807,6 +7899,124 @@ impl<SP: Deref> Channel<SP> where
78077899 }
78087900 }
78097901
7902+ /// Initiate splicing
7903+ #[cfg(splicing)]
7904+ pub fn splice_channel(&mut self, our_funding_contribution_satoshis: i64,
7905+ funding_feerate_perkw: u32, locktime: u32,
7906+ ) -> Result<msgs::SpliceInit, ChannelError> {
7907+ // Check if a splice has been initiated already.
7908+ // Note: this could be handled more nicely, and support multiple outstanding splice's, the incoming splice_ack matters anyways.
7909+ if let Some(splice_info) = &self.context.pending_splice_pre {
7910+ return Err(ChannelError::Warn(format!(
7911+ "Channel has already a splice pending, contribution {}", splice_info.our_funding_contribution
7912+ )));
7913+ }
7914+
7915+ if !matches!(self.context.channel_state, ChannelState::ChannelReady(_)) {
7916+ return Err(ChannelError::Warn(format!("Cannot initiate splicing, as channel is not Ready")));
7917+ }
7918+
7919+ let pre_channel_value = self.context.get_value_satoshis();
7920+ // Sanity check: capacity cannot decrease below 0
7921+ if (pre_channel_value as i64).saturating_add(our_funding_contribution_satoshis) < 0 {
7922+ return Err(ChannelError::Warn(format!(
7923+ "Post-splicing channel value cannot be negative. It was {} + {}",
7924+ pre_channel_value, our_funding_contribution_satoshis
7925+ )));
7926+ }
7927+
7928+ if our_funding_contribution_satoshis < 0 {
7929+ return Err(ChannelError::Warn(format!(
7930+ "TODO(splicing): Splice-out not supported, only splice in, contribution {}",
7931+ our_funding_contribution_satoshis,
7932+ )));
7933+ }
7934+
7935+ // Note: post-splice channel value is not yet known at this point, counterpary contribution is not known
7936+ // (Cannot test for miminum required post-splice channel value)
7937+
7938+ self.context.pending_splice_pre = Some(PendingSpliceInfoPre {
7939+ our_funding_contribution: our_funding_contribution_satoshis,
7940+ });
7941+
7942+ let msg = self.context.get_splice_init(our_funding_contribution_satoshis, funding_feerate_perkw, locktime);
7943+ Ok(msg)
7944+ }
7945+
7946+ /// Handle splice_init
7947+ #[cfg(splicing)]
7948+ pub fn splice_init(&mut self, msg: &msgs::SpliceInit) -> Result<msgs::SpliceAck, ChannelError> {
7949+ let their_funding_contribution_satoshis = msg.funding_contribution_satoshis;
7950+ // TODO(splicing): Currently not possible to contribute on the splicing-acceptor side
7951+ let our_funding_contribution_satoshis = 0i64;
7952+
7953+ // Check if a splice has been initiated already.
7954+ // Note: this could be handled more nicely, and support multiple outstanding splice's, the incoming splice_ack matters anyways.
7955+ if let Some(splice_info) = &self.context.pending_splice_pre {
7956+ return Err(ChannelError::Warn(format!(
7957+ "Channel has already a splice pending, contribution {}", splice_info.our_funding_contribution,
7958+ )));
7959+ }
7960+
7961+ if !matches!(self.context.channel_state, ChannelState::ChannelReady(_)) {
7962+ return Err(ChannelError::Warn(format!("Splicing requested on a channel that is not Ready")));
7963+ }
7964+
7965+ let pre_channel_value = self.context.get_value_satoshis();
7966+ // Sanity check: capacity cannot decrease below 0
7967+ if (pre_channel_value as i64)
7968+ .saturating_add(their_funding_contribution_satoshis)
7969+ .saturating_add(our_funding_contribution_satoshis) < 0
7970+ {
7971+ return Err(ChannelError::Warn(format!(
7972+ "Post-splicing channel value cannot be negative. It was {} + {} + {}",
7973+ pre_channel_value, their_funding_contribution_satoshis, our_funding_contribution_satoshis,
7974+ )));
7975+ }
7976+
7977+ if their_funding_contribution_satoshis.saturating_add(our_funding_contribution_satoshis) < 0 {
7978+ return Err(ChannelError::Warn(format!(
7979+ "Splice-out not supported, only splice in, relative {} + {}",
7980+ their_funding_contribution_satoshis, our_funding_contribution_satoshis,
7981+ )));
7982+ }
7983+
7984+ let post_channel_value = PendingSpliceInfoPre::compute_post_value(pre_channel_value, their_funding_contribution_satoshis, our_funding_contribution_satoshis);
7985+ let post_balance = PendingSpliceInfoPre::add_checked(self.context.value_to_self_msat, our_funding_contribution_satoshis);
7986+ // Early check for reserve requirement, assuming maximum balance of full channel value
7987+ // This will also be checked later at tx_complete
7988+ let _res = self.context.check_balance_meets_reserve_requirements(post_balance, post_channel_value)?;
7989+
7990+ // TODO(splicing): Store msg.funding_pubkey
7991+ // TODO(splicing): Apply start of splice (splice_start)
7992+
7993+ let splice_ack_msg = self.context.get_splice_ack(our_funding_contribution_satoshis);
7994+ // TODO(splicing): start interactive funding negotiation
7995+ Ok(splice_ack_msg)
7996+ }
7997+
7998+ /// Handle splice_ack
7999+ #[cfg(splicing)]
8000+ pub fn splice_ack(&mut self, msg: &msgs::SpliceAck) -> Result<(), ChannelError> {
8001+ let their_funding_contribution_satoshis = msg.funding_contribution_satoshis;
8002+
8003+ // check if splice is pending
8004+ let pending_splice = if let Some(pending_splice) = &self.context.pending_splice_pre {
8005+ pending_splice
8006+ } else {
8007+ return Err(ChannelError::Warn(format!("Channel is not in pending splice")));
8008+ };
8009+
8010+ let our_funding_contribution = pending_splice.our_funding_contribution;
8011+
8012+ let pre_channel_value = self.context.get_value_satoshis();
8013+ let post_channel_value = PendingSpliceInfoPre::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution_satoshis);
8014+ let post_balance = PendingSpliceInfoPre::add_checked(self.context.value_to_self_msat, our_funding_contribution);
8015+ // Early check for reserve requirement, assuming maximum balance of full channel value
8016+ // This will also be checked later at tx_complete
8017+ let _res = self.context.check_balance_meets_reserve_requirements(post_balance, post_channel_value)?;
8018+ Ok(())
8019+ }
78108020
78118021 // Send stuff to our remote peers:
78128022
@@ -10123,6 +10333,9 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
1012310333 // during a signing session, but have not received `tx_signatures` we MUST set `next_funding_txid`
1012410334 // to the txid of that interactive transaction, else we MUST NOT set it.
1012510335 next_funding_txid,
10336+
10337+ #[cfg(splicing)]
10338+ pending_splice_pre: None,
1012610339 },
1012710340 interactive_tx_signing_session: None,
1012810341 })
@@ -11907,4 +12120,69 @@ mod tests {
1190712120 assert_eq!(node_a_chan.context.channel_state, ChannelState::AwaitingChannelReady(AwaitingChannelReadyFlags::THEIR_CHANNEL_READY));
1190812121 assert!(node_a_chan.check_get_channel_ready(0, &&logger).is_some());
1190912122 }
12123+
12124+ #[cfg(all(test, splicing))]
12125+ fn get_pre_and_post(pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64) -> (u64, u64) {
12126+ use crate::ln::channel::PendingSpliceInfoPre;
12127+
12128+ let post_channel_value = PendingSpliceInfoPre::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution);
12129+ (pre_channel_value, post_channel_value)
12130+ }
12131+
12132+ #[cfg(all(test, splicing))]
12133+ #[test]
12134+ fn test_splice_compute_post_value() {
12135+ {
12136+ // increase, small amounts
12137+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 6_000, 0);
12138+ assert_eq!(pre_channel_value, 9_000);
12139+ assert_eq!(post_channel_value, 15_000);
12140+ }
12141+ {
12142+ // increase, small amounts
12143+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 4_000, 2_000);
12144+ assert_eq!(pre_channel_value, 9_000);
12145+ assert_eq!(post_channel_value, 15_000);
12146+ }
12147+ {
12148+ // increase, small amounts
12149+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 0, 6_000);
12150+ assert_eq!(pre_channel_value, 9_000);
12151+ assert_eq!(post_channel_value, 15_000);
12152+ }
12153+ {
12154+ // decrease, small amounts
12155+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -6_000, 0);
12156+ assert_eq!(pre_channel_value, 15_000);
12157+ assert_eq!(post_channel_value, 9_000);
12158+ }
12159+ {
12160+ // decrease, small amounts
12161+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -4_000, -2_000);
12162+ assert_eq!(pre_channel_value, 15_000);
12163+ assert_eq!(post_channel_value, 9_000);
12164+ }
12165+ {
12166+ // increase and decrease
12167+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, 4_000, -2_000);
12168+ assert_eq!(pre_channel_value, 15_000);
12169+ assert_eq!(post_channel_value, 17_000);
12170+ }
12171+ let base2: u64 = 2;
12172+ let huge63i3 = (base2.pow(63) - 3) as i64;
12173+ assert_eq!(huge63i3, 9223372036854775805);
12174+ assert_eq!(-huge63i3, -9223372036854775805);
12175+ {
12176+ // increase, large amount
12177+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, 3);
12178+ assert_eq!(pre_channel_value, 9_000);
12179+ assert_eq!(post_channel_value, 9223372036854784807);
12180+ }
12181+ {
12182+ // increase, large amounts
12183+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, huge63i3);
12184+ assert_eq!(pre_channel_value, 9_000);
12185+ assert_eq!(post_channel_value, 9223372036854784807);
12186+ }
12187+ }
1191012188}
0 commit comments