@@ -1519,7 +1519,7 @@ impl<SP: Deref> Channel<SP> where
15191519 holder_commitment_point,
15201520 is_v2_established: true,
15211521 #[cfg(splicing)]
1522- pending_splice : None,
1522+ pending_splice_pre : None,
15231523 };
15241524 let res = funded_channel.commitment_signed_initial_v2(msg, best_block, signer_provider, logger)
15251525 .map(|monitor| (Some(monitor), None))
@@ -1728,6 +1728,23 @@ struct PendingSplice {
17281728 pub our_funding_contribution: i64,
17291729}
17301730
1731+ #[cfg(splicing)]
1732+ impl PendingSplice {
1733+ #[inline]
1734+ fn add_checked(base: u64, delta: i64) -> u64 {
1735+ if delta >= 0 {
1736+ base.saturating_add(delta as u64)
1737+ } else {
1738+ base.saturating_sub(delta.abs() as u64)
1739+ }
1740+ }
1741+
1742+ /// Compute the post-splice channel value from the pre-splice values and the peer contributions
1743+ pub fn compute_post_value(pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64) -> u64 {
1744+ Self::add_checked(pre_channel_value, our_funding_contribution.saturating_add(their_funding_contribution))
1745+ }
1746+ }
1747+
17311748/// Contains everything about the channel including state, and various flags.
17321749pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
17331750 config: LegacyChannelConfig,
@@ -4256,6 +4273,33 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
42564273 }
42574274 }
42584275
4276+ /// Check that a balance value meets the channel reserve requirements or violates them (below reserve).
4277+ /// The channel value is an input as opposed to using from self, so that this can be used in case of splicing
4278+ /// to checks with new channel value (before being comitted to it).
4279+ #[cfg(splicing)]
4280+ pub fn check_balance_meets_reserve_requirements(&self, balance: u64, channel_value: u64) -> Result<(), ChannelError> {
4281+ if balance == 0 {
4282+ return Ok(());
4283+ }
4284+ let holder_selected_channel_reserve_satoshis = get_v2_channel_reserve_satoshis(
4285+ channel_value, self.holder_dust_limit_satoshis);
4286+ if balance < holder_selected_channel_reserve_satoshis {
4287+ return Err(ChannelError::Warn(format!(
4288+ "Balance below reserve mandated by holder, {} vs {}",
4289+ balance, holder_selected_channel_reserve_satoshis,
4290+ )));
4291+ }
4292+ let counterparty_selected_channel_reserve_satoshis = get_v2_channel_reserve_satoshis(
4293+ channel_value, self.counterparty_dust_limit_satoshis);
4294+ if balance < counterparty_selected_channel_reserve_satoshis {
4295+ return Err(ChannelError::Warn(format!(
4296+ "Balance below reserve mandated by counterparty, {} vs {}",
4297+ balance, counterparty_selected_channel_reserve_satoshis,
4298+ )));
4299+ }
4300+ Ok(())
4301+ }
4302+
42594303 /// Get the commitment tx fee for the local's (i.e. our) next commitment transaction based on the
42604304 /// number of pending HTLCs that are on track to be in our next commitment tx.
42614305 ///
@@ -4902,7 +4946,7 @@ pub(super) struct FundedChannel<SP: Deref> where SP::Target: SignerProvider {
49024946 is_v2_established: bool,
49034947 /// Info about an in-progress, pending splice (if any), on the pre-splice channel
49044948 #[cfg(splicing)]
4905- pending_splice : Option<PendingSplice>,
4949+ pending_splice_pre : Option<PendingSplice>,
49064950}
49074951
49084952#[cfg(any(test, fuzzing))]
@@ -8435,7 +8479,7 @@ impl<SP: Deref> FundedChannel<SP> where
84358479 ) -> Result<msgs::SpliceInit, APIError> {
84368480 // Check if a splice has been initiated already.
84378481 // Note: only a single outstanding splice is supported (per spec)
8438- if let Some(splice_info) = &self.pending_splice {
8482+ if let Some(splice_info) = &self.pending_splice_pre {
84398483 return Err(APIError::APIMisuseError { err: format!(
84408484 "Channel {} cannot be spliced, as it has already a splice pending (contribution {})",
84418485 self.context.channel_id(), splice_info.our_funding_contribution
@@ -8471,7 +8515,7 @@ impl<SP: Deref> FundedChannel<SP> where
84718515 self.context.channel_id(), err,
84728516 )})?;
84738517
8474- self.pending_splice = Some(PendingSplice {
8518+ self.pending_splice_pre = Some(PendingSplice {
84758519 our_funding_contribution: our_funding_contribution_satoshis,
84768520 });
84778521
@@ -8505,7 +8549,7 @@ impl<SP: Deref> FundedChannel<SP> where
85058549 let our_funding_contribution_satoshis = 0i64;
85068550
85078551 // Check if a splice has been initiated already.
8508- if let Some(splice_info) = &self.pending_splice {
8552+ if let Some(splice_info) = &self.pending_splice_pre {
85098553 return Err(ChannelError::Warn(format!(
85108554 "Channel has already a splice pending, contribution {}", splice_info.our_funding_contribution,
85118555 )));
@@ -8531,7 +8575,13 @@ impl<SP: Deref> FundedChannel<SP> where
85318575
85328576 // Note on channel reserve requirement pre-check: as the splice acceptor does not contribute,
85338577 // it can't go below reserve, therefore no pre-check is done here.
8534- // TODO(splicing): Once splice acceptor can contribute, add reserve pre-check, similar to the one in `splice_ack`.
8578+
8579+ let pre_channel_value = self.funding.value_to_self_msat;
8580+ let post_channel_value = PendingSplice::compute_post_value(pre_channel_value, their_funding_contribution_satoshis, our_funding_contribution_satoshis);
8581+ let post_balance = PendingSplice::add_checked(self.funding.value_to_self_msat, our_funding_contribution_satoshis);
8582+ // Early check for reserve requirement, assuming maximum balance of full channel value
8583+ // This will also be checked later at tx_complete
8584+ let _res = self.context.check_balance_meets_reserve_requirements(post_balance, post_channel_value)?;
85358585
85368586 // TODO(splicing): Store msg.funding_pubkey
85378587 // TODO(splicing): Apply start of splice (splice_start)
@@ -8550,14 +8600,27 @@ impl<SP: Deref> FundedChannel<SP> where
85508600
85518601 /// Handle splice_ack
85528602 #[cfg(splicing)]
8553- pub fn splice_ack(&mut self, _msg : &msgs::SpliceAck) -> Result<(), ChannelError> {
8603+ pub fn splice_ack(&mut self, msg : &msgs::SpliceAck) -> Result<(), ChannelError> {
85548604 // check if splice is pending
8555- if self.pending_splice.is_none() {
8605+ let pending_splice = if let Some(pending_splice) = &self.pending_splice_pre {
8606+ pending_splice
8607+ } else {
85568608 return Err(ChannelError::Warn(format!("Channel is not in pending splice")));
85578609 };
85588610
85598611 // TODO(splicing): Pre-check for reserve requirement
85608612 // (Note: It should also be checked later at tx_complete)
8613+
8614+
8615+ let our_funding_contribution = pending_splice.our_funding_contribution;
8616+ let their_funding_contribution_satoshis = msg.funding_contribution_satoshis;
8617+
8618+ let pre_channel_value = self.funding.get_value_satoshis();
8619+ let post_channel_value = PendingSplice::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution_satoshis);
8620+ let post_balance = PendingSplice::add_checked(self.funding.value_to_self_msat, our_funding_contribution);
8621+ // Early check for reserve requirement, assuming maximum balance of full channel value
8622+ // This will also be checked later at tx_complete
8623+ let _res = self.context.check_balance_meets_reserve_requirements(post_balance, post_channel_value)?;
85618624 Ok(())
85628625 }
85638626
@@ -9481,7 +9544,7 @@ impl<SP: Deref> OutboundV1Channel<SP> where SP::Target: SignerProvider {
94819544 is_v2_established: false,
94829545 holder_commitment_point,
94839546 #[cfg(splicing)]
9484- pending_splice : None,
9547+ pending_splice_pre : None,
94859548 };
94869549
94879550 let need_channel_ready = channel.check_get_channel_ready(0, logger).is_some()
@@ -9758,7 +9821,7 @@ impl<SP: Deref> InboundV1Channel<SP> where SP::Target: SignerProvider {
97589821 is_v2_established: false,
97599822 holder_commitment_point,
97609823 #[cfg(splicing)]
9761- pending_splice : None,
9824+ pending_splice_pre : None,
97629825 };
97639826 let need_channel_ready = channel.check_get_channel_ready(0, logger).is_some()
97649827 || channel.context.signer_pending_channel_ready;
@@ -11120,7 +11183,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, &'c Channel
1112011183 is_v2_established,
1112111184 holder_commitment_point,
1112211185 #[cfg(splicing)]
11123- pending_splice : None,
11186+ pending_splice_pre : None,
1112411187 })
1112511188 }
1112611189}
@@ -13050,4 +13113,69 @@ mod tests {
1305013113 );
1305113114 }
1305213115 }
13116+
13117+ #[cfg(all(test, splicing))]
13118+ fn get_pre_and_post(pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64) -> (u64, u64) {
13119+ use crate::ln::channel::PendingSplice;
13120+
13121+ let post_channel_value = PendingSplice::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution);
13122+ (pre_channel_value, post_channel_value)
13123+ }
13124+
13125+ #[cfg(all(test, splicing))]
13126+ #[test]
13127+ fn test_splice_compute_post_value() {
13128+ {
13129+ // increase, small amounts
13130+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 6_000, 0);
13131+ assert_eq!(pre_channel_value, 9_000);
13132+ assert_eq!(post_channel_value, 15_000);
13133+ }
13134+ {
13135+ // increase, small amounts
13136+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 4_000, 2_000);
13137+ assert_eq!(pre_channel_value, 9_000);
13138+ assert_eq!(post_channel_value, 15_000);
13139+ }
13140+ {
13141+ // increase, small amounts
13142+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 0, 6_000);
13143+ assert_eq!(pre_channel_value, 9_000);
13144+ assert_eq!(post_channel_value, 15_000);
13145+ }
13146+ {
13147+ // decrease, small amounts
13148+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -6_000, 0);
13149+ assert_eq!(pre_channel_value, 15_000);
13150+ assert_eq!(post_channel_value, 9_000);
13151+ }
13152+ {
13153+ // decrease, small amounts
13154+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -4_000, -2_000);
13155+ assert_eq!(pre_channel_value, 15_000);
13156+ assert_eq!(post_channel_value, 9_000);
13157+ }
13158+ {
13159+ // increase and decrease
13160+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, 4_000, -2_000);
13161+ assert_eq!(pre_channel_value, 15_000);
13162+ assert_eq!(post_channel_value, 17_000);
13163+ }
13164+ let base2: u64 = 2;
13165+ let huge63i3 = (base2.pow(63) - 3) as i64;
13166+ assert_eq!(huge63i3, 9223372036854775805);
13167+ assert_eq!(-huge63i3, -9223372036854775805);
13168+ {
13169+ // increase, large amount
13170+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, 3);
13171+ assert_eq!(pre_channel_value, 9_000);
13172+ assert_eq!(post_channel_value, 9223372036854784807);
13173+ }
13174+ {
13175+ // increase, large amounts
13176+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, huge63i3);
13177+ assert_eq!(pre_channel_value, 9_000);
13178+ assert_eq!(post_channel_value, 9223372036854784807);
13179+ }
13180+ }
1305313181}
0 commit comments