@@ -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};
@@ -1649,6 +1648,30 @@ impl FundingScope {
16491648 }
16501649}
16511650
1651+ /// Info about a pending splice, used in the pre-splice channel
1652+ #[cfg(splicing)]
1653+ #[derive(Clone)]
1654+ struct PendingSplice {
1655+ pub our_funding_contribution: i64,
1656+ }
1657+
1658+ #[cfg(splicing)]
1659+ impl PendingSplice {
1660+ #[inline]
1661+ fn add_checked(base: u64, delta: i64) -> u64 {
1662+ if delta >= 0 {
1663+ base.saturating_add(delta as u64)
1664+ } else {
1665+ base.saturating_sub(delta.abs() as u64)
1666+ }
1667+ }
1668+
1669+ /// Compute the post-splice channel value from the pre-splice values and the peer contributions
1670+ pub fn compute_post_value(pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64) -> u64 {
1671+ Self::add_checked(pre_channel_value, our_funding_contribution.saturating_add(their_funding_contribution))
1672+ }
1673+ }
1674+
16521675/// Contains everything about the channel including state, and various flags.
16531676pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
16541677 config: LegacyChannelConfig,
@@ -2290,6 +2313,8 @@ impl<SP: Deref> PendingV2Channel<SP> where SP::Target: SignerProvider {
22902313 context: self.context,
22912314 interactive_tx_signing_session: Some(signing_session),
22922315 holder_commitment_point,
2316+ #[cfg(splicing)]
2317+ pending_splice_pre: None,
22932318 };
22942319 Ok((funded_chan, commitment_signed, funding_ready_for_sig_event))
22952320 },
@@ -4068,6 +4093,33 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
40684093 }
40694094 }
40704095
4096+ /// Check that a balance value meets the channel reserve requirements or violates them (below reserve).
4097+ /// The channel value is an input as opposed to using from self, so that this can be used in case of splicing
4098+ /// to checks with new channel value (before being comitted to it).
4099+ #[cfg(splicing)]
4100+ pub fn check_balance_meets_reserve_requirements(&self, balance: u64, channel_value: u64) -> Result<(), ChannelError> {
4101+ if balance == 0 {
4102+ return Ok(());
4103+ }
4104+ let holder_selected_channel_reserve_satoshis = get_v2_channel_reserve_satoshis(
4105+ channel_value, self.holder_dust_limit_satoshis);
4106+ if balance < holder_selected_channel_reserve_satoshis {
4107+ return Err(ChannelError::Warn(format!(
4108+ "Balance below reserve mandated by holder, {} vs {}",
4109+ balance, holder_selected_channel_reserve_satoshis,
4110+ )));
4111+ }
4112+ let counterparty_selected_channel_reserve_satoshis = get_v2_channel_reserve_satoshis(
4113+ channel_value, self.counterparty_dust_limit_satoshis);
4114+ if balance < counterparty_selected_channel_reserve_satoshis {
4115+ return Err(ChannelError::Warn(format!(
4116+ "Balance below reserve mandated by counterparty, {} vs {}",
4117+ balance, counterparty_selected_channel_reserve_satoshis,
4118+ )));
4119+ }
4120+ Ok(())
4121+ }
4122+
40714123 /// Get the commitment tx fee for the local's (i.e. our) next commitment transaction based on the
40724124 /// number of pending HTLCs that are on track to be in our next commitment tx.
40734125 ///
@@ -4538,6 +4590,38 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
45384590 self.channel_transaction_parameters = channel_transaction_parameters;
45394591 self.get_initial_counterparty_commitment_signature(funding, logger)
45404592 }
4593+
4594+ /// Get the splice message that can be sent during splice initiation.
4595+ #[cfg(splicing)]
4596+ pub fn get_splice_init(&self, our_funding_contribution_satoshis: i64,
4597+ funding_feerate_perkw: u32, locktime: u32,
4598+ ) -> msgs::SpliceInit {
4599+ // Reuse the existing funding pubkey, in spite of the channel value changing
4600+ // (though at this point we don't know the new value yet, due tue the optional counterparty contribution)
4601+ // Note that channel_keys_id is supposed NOT to change
4602+ let funding_pubkey = self.get_holder_pubkeys().funding_pubkey.clone();
4603+ msgs::SpliceInit {
4604+ channel_id: self.channel_id,
4605+ funding_contribution_satoshis: our_funding_contribution_satoshis,
4606+ funding_feerate_perkw,
4607+ locktime,
4608+ funding_pubkey,
4609+ require_confirmed_inputs: None,
4610+ }
4611+ }
4612+
4613+ /// Get the splice_ack message that can be sent in response to splice initiation.
4614+ #[cfg(splicing)]
4615+ pub fn get_splice_ack(&self, our_funding_contribution_satoshis: i64) -> msgs::SpliceAck {
4616+ // Reuse the existing funding pubkey, in spite of the channel value changing
4617+ let funding_pubkey = self.get_holder_pubkeys().funding_pubkey;
4618+ msgs::SpliceAck {
4619+ channel_id: self.channel_id,
4620+ funding_contribution_satoshis: our_funding_contribution_satoshis,
4621+ funding_pubkey,
4622+ require_confirmed_inputs: None,
4623+ }
4624+ }
45414625}
45424626
45434627// Internal utility functions for channels
@@ -4650,6 +4734,9 @@ pub(super) struct FundedChannel<SP: Deref> where SP::Target: SignerProvider {
46504734 pub context: ChannelContext<SP>,
46514735 pub interactive_tx_signing_session: Option<InteractiveTxSigningSession>,
46524736 holder_commitment_point: HolderCommitmentPoint,
4737+ /// Info about an in-progress, pending splice (if any), on the pre-splice channel
4738+ #[cfg(splicing)]
4739+ pending_splice_pre: Option<PendingSplice>,
46534740}
46544741
46554742#[cfg(any(test, fuzzing))]
@@ -8277,6 +8364,135 @@ impl<SP: Deref> FundedChannel<SP> where
82778364 }
82788365 }
82798366
8367+ /// Initiate splicing
8368+ #[cfg(splicing)]
8369+ pub fn splice_channel(&mut self, our_funding_contribution_satoshis: i64,
8370+ our_funding_inputs: Vec<(TxIn, Transaction)>, funding_feerate_perkw: u32, locktime: u32,
8371+ ) -> Result<msgs::SpliceInit, ChannelError> {
8372+ // Check if a splice has been initiated already.
8373+ // Note: this could be handled more nicely, and support multiple outstanding splice's, the incoming splice_ack matters anyways.
8374+ if let Some(splice_info) = &self.pending_splice_pre {
8375+ return Err(ChannelError::Warn(format!(
8376+ "Channel has already a splice pending, contribution {}", splice_info.our_funding_contribution
8377+ )));
8378+ }
8379+
8380+ if !matches!(self.context.channel_state, ChannelState::ChannelReady(_)) {
8381+ return Err(ChannelError::Warn(format!("Cannot initiate splicing, as channel is not Ready")));
8382+ }
8383+
8384+ let pre_channel_value = self.funding.get_value_satoshis();
8385+ // Sanity check: capacity cannot decrease below 0
8386+ if (pre_channel_value as i64).saturating_add(our_funding_contribution_satoshis) < 0 {
8387+ return Err(ChannelError::Warn(format!(
8388+ "Post-splicing channel value cannot be negative. It was {} + {}",
8389+ pre_channel_value, our_funding_contribution_satoshis
8390+ )));
8391+ }
8392+
8393+ if our_funding_contribution_satoshis < 0 {
8394+ return Err(ChannelError::Warn(format!(
8395+ "TODO(splicing): Splice-out not supported, only splice in, contribution {}",
8396+ our_funding_contribution_satoshis,
8397+ )));
8398+ }
8399+
8400+ // Note: post-splice channel value is not yet known at this point, counterpary contribution is not known
8401+ // (Cannot test for miminum required post-splice channel value)
8402+
8403+ // Check that inputs are sufficient to cover our contribution
8404+ let sum_input: i64 = our_funding_inputs.into_iter().map(
8405+ |(txin, tx)| tx.output.get(txin.previous_output.vout as usize).map(|tx| tx.value.to_sat() as i64).unwrap_or(0)
8406+ ).sum();
8407+ if sum_input < our_funding_contribution_satoshis {
8408+ return Err(ChannelError::Warn(format!(
8409+ "Provided inputs are insufficient for our contribution, {} {}",
8410+ sum_input, our_funding_contribution_satoshis,
8411+ )));
8412+ }
8413+
8414+ self.pending_splice_pre = Some(PendingSplice {
8415+ our_funding_contribution: our_funding_contribution_satoshis,
8416+ });
8417+
8418+ let msg = self.context.get_splice_init(our_funding_contribution_satoshis, funding_feerate_perkw, locktime);
8419+ Ok(msg)
8420+ }
8421+
8422+ /// Handle splice_init
8423+ #[cfg(splicing)]
8424+ pub fn splice_init(&mut self, msg: &msgs::SpliceInit) -> Result<msgs::SpliceAck, ChannelError> {
8425+ let their_funding_contribution_satoshis = msg.funding_contribution_satoshis;
8426+ // TODO(splicing): Currently not possible to contribute on the splicing-acceptor side
8427+ let our_funding_contribution_satoshis = 0i64;
8428+
8429+ // Check if a splice has been initiated already.
8430+ // Note: this could be handled more nicely, and support multiple outstanding splice's, the incoming splice_ack matters anyways.
8431+ if let Some(splice_info) = &self.pending_splice_pre {
8432+ return Err(ChannelError::Warn(format!(
8433+ "Channel has already a splice pending, contribution {}", splice_info.our_funding_contribution,
8434+ )));
8435+ }
8436+
8437+ if !matches!(self.context.channel_state, ChannelState::ChannelReady(_)) {
8438+ return Err(ChannelError::Warn(format!("Splicing requested on a channel that is not Ready")));
8439+ }
8440+
8441+ let pre_channel_value = self.funding.get_value_satoshis();
8442+ // Sanity check: capacity cannot decrease below 0
8443+ if (pre_channel_value as i64)
8444+ .saturating_add(their_funding_contribution_satoshis)
8445+ .saturating_add(our_funding_contribution_satoshis) < 0
8446+ {
8447+ return Err(ChannelError::Warn(format!(
8448+ "Post-splicing channel value cannot be negative. It was {} + {} + {}",
8449+ pre_channel_value, their_funding_contribution_satoshis, our_funding_contribution_satoshis,
8450+ )));
8451+ }
8452+
8453+ if their_funding_contribution_satoshis.saturating_add(our_funding_contribution_satoshis) < 0 {
8454+ return Err(ChannelError::Warn(format!(
8455+ "Splice-out not supported, only splice in, relative {} + {}",
8456+ their_funding_contribution_satoshis, our_funding_contribution_satoshis,
8457+ )));
8458+ }
8459+
8460+ let post_channel_value = PendingSplice::compute_post_value(pre_channel_value, their_funding_contribution_satoshis, our_funding_contribution_satoshis);
8461+ let post_balance = PendingSplice::add_checked(self.funding.value_to_self_msat, our_funding_contribution_satoshis);
8462+ // Early check for reserve requirement, assuming maximum balance of full channel value
8463+ // This will also be checked later at tx_complete
8464+ let _res = self.context.check_balance_meets_reserve_requirements(post_balance, post_channel_value)?;
8465+
8466+ // TODO(splicing): Store msg.funding_pubkey
8467+ // TODO(splicing): Apply start of splice (splice_start)
8468+
8469+ let splice_ack_msg = self.context.get_splice_ack(our_funding_contribution_satoshis);
8470+ // TODO(splicing): start interactive funding negotiation
8471+ Ok(splice_ack_msg)
8472+ }
8473+
8474+ /// Handle splice_ack
8475+ #[cfg(splicing)]
8476+ pub fn splice_ack(&mut self, msg: &msgs::SpliceAck) -> Result<(), ChannelError> {
8477+ let their_funding_contribution_satoshis = msg.funding_contribution_satoshis;
8478+
8479+ // check if splice is pending
8480+ let pending_splice = if let Some(pending_splice) = &self.pending_splice_pre {
8481+ pending_splice
8482+ } else {
8483+ return Err(ChannelError::Warn(format!("Channel is not in pending splice")));
8484+ };
8485+
8486+ let our_funding_contribution = pending_splice.our_funding_contribution;
8487+
8488+ let pre_channel_value = self.funding.get_value_satoshis();
8489+ let post_channel_value = PendingSplice::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution_satoshis);
8490+ let post_balance = PendingSplice::add_checked(self.funding.value_to_self_msat, our_funding_contribution);
8491+ // Early check for reserve requirement, assuming maximum balance of full channel value
8492+ // This will also be checked later at tx_complete
8493+ let _res = self.context.check_balance_meets_reserve_requirements(post_balance, post_channel_value)?;
8494+ Ok(())
8495+ }
82808496
82818497 // Send stuff to our remote peers:
82828498
@@ -9191,6 +9407,8 @@ impl<SP: Deref> OutboundV1Channel<SP> where SP::Target: SignerProvider {
91919407 context: self.context,
91929408 interactive_tx_signing_session: None,
91939409 holder_commitment_point,
9410+ #[cfg(splicing)]
9411+ pending_splice_pre: None,
91949412 };
91959413
91969414 let need_channel_ready = channel.check_get_channel_ready(0, logger).is_some()
@@ -9458,6 +9676,8 @@ impl<SP: Deref> InboundV1Channel<SP> where SP::Target: SignerProvider {
94589676 context: self.context,
94599677 interactive_tx_signing_session: None,
94609678 holder_commitment_point,
9679+ #[cfg(splicing)]
9680+ pending_splice_pre: None,
94619681 };
94629682 let need_channel_ready = channel.check_get_channel_ready(0, logger).is_some()
94639683 || channel.context.signer_pending_channel_ready;
@@ -10849,6 +11069,8 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
1084911069 },
1085011070 interactive_tx_signing_session: None,
1085111071 holder_commitment_point,
11072+ #[cfg(splicing)]
11073+ pending_splice_pre: None,
1085211074 })
1085311075 }
1085411076}
@@ -12667,4 +12889,69 @@ mod tests {
1266712889 320
1266812890 );
1266912891 }
12892+
12893+ #[cfg(all(test, splicing))]
12894+ fn get_pre_and_post(pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64) -> (u64, u64) {
12895+ use crate::ln::channel::PendingSplice;
12896+
12897+ let post_channel_value = PendingSplice::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution);
12898+ (pre_channel_value, post_channel_value)
12899+ }
12900+
12901+ #[cfg(all(test, splicing))]
12902+ #[test]
12903+ fn test_splice_compute_post_value() {
12904+ {
12905+ // increase, small amounts
12906+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 6_000, 0);
12907+ assert_eq!(pre_channel_value, 9_000);
12908+ assert_eq!(post_channel_value, 15_000);
12909+ }
12910+ {
12911+ // increase, small amounts
12912+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 4_000, 2_000);
12913+ assert_eq!(pre_channel_value, 9_000);
12914+ assert_eq!(post_channel_value, 15_000);
12915+ }
12916+ {
12917+ // increase, small amounts
12918+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 0, 6_000);
12919+ assert_eq!(pre_channel_value, 9_000);
12920+ assert_eq!(post_channel_value, 15_000);
12921+ }
12922+ {
12923+ // decrease, small amounts
12924+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -6_000, 0);
12925+ assert_eq!(pre_channel_value, 15_000);
12926+ assert_eq!(post_channel_value, 9_000);
12927+ }
12928+ {
12929+ // decrease, small amounts
12930+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -4_000, -2_000);
12931+ assert_eq!(pre_channel_value, 15_000);
12932+ assert_eq!(post_channel_value, 9_000);
12933+ }
12934+ {
12935+ // increase and decrease
12936+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, 4_000, -2_000);
12937+ assert_eq!(pre_channel_value, 15_000);
12938+ assert_eq!(post_channel_value, 17_000);
12939+ }
12940+ let base2: u64 = 2;
12941+ let huge63i3 = (base2.pow(63) - 3) as i64;
12942+ assert_eq!(huge63i3, 9223372036854775805);
12943+ assert_eq!(-huge63i3, -9223372036854775805);
12944+ {
12945+ // increase, large amount
12946+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, 3);
12947+ assert_eq!(pre_channel_value, 9_000);
12948+ assert_eq!(post_channel_value, 9223372036854784807);
12949+ }
12950+ {
12951+ // increase, large amounts
12952+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, huge63i3);
12953+ assert_eq!(pre_channel_value, 9_000);
12954+ assert_eq!(post_channel_value, 9223372036854784807);
12955+ }
12956+ }
1267012957}
0 commit comments