@@ -1522,7 +1522,7 @@ impl<SP: Deref> Channel<SP> where
15221522 holder_commitment_point,
15231523 is_v2_established: true,
15241524 #[cfg(splicing)]
1525- pending_splice : None,
1525+ pending_splice_pre : None,
15261526 };
15271527 let res = funded_channel.commitment_signed_initial_v2(msg, best_block, signer_provider, logger)
15281528 .map(|monitor| (Some(monitor), None))
@@ -1736,6 +1736,23 @@ struct PendingSplice {
17361736 pub our_funding_contribution: i64,
17371737}
17381738
1739+ #[cfg(splicing)]
1740+ impl PendingSplice {
1741+ #[inline]
1742+ fn add_checked(base: u64, delta: i64) -> u64 {
1743+ if delta >= 0 {
1744+ base.saturating_add(delta as u64)
1745+ } else {
1746+ base.saturating_sub(delta.abs() as u64)
1747+ }
1748+ }
1749+
1750+ /// Compute the post-splice channel value from the pre-splice values and the peer contributions
1751+ pub fn compute_post_value(pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64) -> u64 {
1752+ Self::add_checked(pre_channel_value, our_funding_contribution.saturating_add(their_funding_contribution))
1753+ }
1754+ }
1755+
17391756/// Contains everything about the channel including state, and various flags.
17401757pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
17411758 config: LegacyChannelConfig,
@@ -4299,6 +4316,33 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
42994316 }
43004317 }
43014318
4319+ /// Check that a balance value meets the channel reserve requirements or violates them (below reserve).
4320+ /// The channel value is an input as opposed to using from self, so that this can be used in case of splicing
4321+ /// to checks with new channel value (before being comitted to it).
4322+ #[cfg(splicing)]
4323+ pub fn check_balance_meets_v2_reserve_requirements(&self, balance: u64, channel_value: u64) -> Result<(), ChannelError> {
4324+ if balance == 0 {
4325+ return Ok(());
4326+ }
4327+ let holder_selected_channel_reserve_satoshis = get_v2_channel_reserve_satoshis(
4328+ channel_value, self.holder_dust_limit_satoshis);
4329+ if balance < holder_selected_channel_reserve_satoshis {
4330+ return Err(ChannelError::Warn(format!(
4331+ "Balance below reserve mandated by holder, {} vs {}",
4332+ balance, holder_selected_channel_reserve_satoshis,
4333+ )));
4334+ }
4335+ let counterparty_selected_channel_reserve_satoshis = get_v2_channel_reserve_satoshis(
4336+ channel_value, self.counterparty_dust_limit_satoshis);
4337+ if balance < counterparty_selected_channel_reserve_satoshis {
4338+ return Err(ChannelError::Warn(format!(
4339+ "Balance below reserve mandated by counterparty, {} vs {}",
4340+ balance, counterparty_selected_channel_reserve_satoshis,
4341+ )));
4342+ }
4343+ Ok(())
4344+ }
4345+
43024346 /// Get the commitment tx fee for the local's (i.e. our) next commitment transaction based on the
43034347 /// number of pending HTLCs that are on track to be in our next commitment tx.
43044348 ///
@@ -4951,7 +4995,7 @@ pub(super) struct FundedChannel<SP: Deref> where SP::Target: SignerProvider {
49514995 is_v2_established: bool,
49524996 /// Info about an in-progress, pending splice (if any), on the pre-splice channel
49534997 #[cfg(splicing)]
4954- pending_splice : Option<PendingSplice>,
4998+ pending_splice_pre : Option<PendingSplice>,
49554999}
49565000
49575001#[cfg(any(test, fuzzing))]
@@ -8487,7 +8531,7 @@ impl<SP: Deref> FundedChannel<SP> where
84878531 ) -> Result<msgs::SpliceInit, APIError> {
84888532 // Check if a splice has been initiated already.
84898533 // Note: only a single outstanding splice is supported (per spec)
8490- if let Some(splice_info) = &self.pending_splice {
8534+ if let Some(splice_info) = &self.pending_splice_pre {
84918535 return Err(APIError::APIMisuseError { err: format!(
84928536 "Channel {} cannot be spliced, as it has already a splice pending (contribution {})",
84938537 self.context.channel_id(), splice_info.our_funding_contribution
@@ -8523,7 +8567,7 @@ impl<SP: Deref> FundedChannel<SP> where
85238567 self.context.channel_id(), err,
85248568 )})?;
85258569
8526- self.pending_splice = Some(PendingSplice {
8570+ self.pending_splice_pre = Some(PendingSplice {
85278571 our_funding_contribution: our_funding_contribution_satoshis,
85288572 });
85298573
@@ -8557,7 +8601,7 @@ impl<SP: Deref> FundedChannel<SP> where
85578601 let our_funding_contribution_satoshis = 0i64;
85588602
85598603 // Check if a splice has been initiated already.
8560- if let Some(splice_info) = &self.pending_splice {
8604+ if let Some(splice_info) = &self.pending_splice_pre {
85618605 return Err(ChannelError::Warn(format!(
85628606 "Channel has already a splice pending, contribution {}", splice_info.our_funding_contribution,
85638607 )));
@@ -8583,7 +8627,13 @@ impl<SP: Deref> FundedChannel<SP> where
85838627
85848628 // Note on channel reserve requirement pre-check: as the splice acceptor does not contribute,
85858629 // it can't go below reserve, therefore no pre-check is done here.
8586- // TODO(splicing): Once splice acceptor can contribute, add reserve pre-check, similar to the one in `splice_ack`.
8630+
8631+ let pre_channel_value = self.funding.value_to_self_msat;
8632+ let post_channel_value = PendingSplice::compute_post_value(pre_channel_value, their_funding_contribution_satoshis, our_funding_contribution_satoshis);
8633+ let post_balance = PendingSplice::add_checked(self.funding.value_to_self_msat, our_funding_contribution_satoshis);
8634+ // Early check for reserve requirement, assuming maximum balance of full channel value
8635+ // This will also be checked later at tx_complete
8636+ let _res = self.context.check_balance_meets_v2_reserve_requirements(post_balance, post_channel_value)?;
85878637
85888638 // TODO(splicing): Store msg.funding_pubkey
85898639 // TODO(splicing): Apply start of splice (splice_start)
@@ -8602,14 +8652,27 @@ impl<SP: Deref> FundedChannel<SP> where
86028652
86038653 /// Handle splice_ack
86048654 #[cfg(splicing)]
8605- pub fn splice_ack(&mut self, _msg : &msgs::SpliceAck) -> Result<(), ChannelError> {
8655+ pub fn splice_ack(&mut self, msg : &msgs::SpliceAck) -> Result<(), ChannelError> {
86068656 // check if splice is pending
8607- if self.pending_splice.is_none() {
8657+ let pending_splice = if let Some(pending_splice) = &self.pending_splice_pre {
8658+ pending_splice
8659+ } else {
86088660 return Err(ChannelError::Warn(format!("Channel is not in pending splice")));
86098661 };
86108662
86118663 // TODO(splicing): Pre-check for reserve requirement
86128664 // (Note: It should also be checked later at tx_complete)
8665+
8666+
8667+ let our_funding_contribution = pending_splice.our_funding_contribution;
8668+ let their_funding_contribution_satoshis = msg.funding_contribution_satoshis;
8669+
8670+ let pre_channel_value = self.funding.get_value_satoshis();
8671+ let post_channel_value = PendingSplice::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution_satoshis);
8672+ let post_balance = PendingSplice::add_checked(self.funding.value_to_self_msat, our_funding_contribution);
8673+ // Early check for reserve requirement, assuming maximum balance of full channel value
8674+ // This will also be checked later at tx_complete
8675+ let _res = self.context.check_balance_meets_v2_reserve_requirements(post_balance, post_channel_value)?;
86138676 Ok(())
86148677 }
86158678
@@ -9536,7 +9599,7 @@ impl<SP: Deref> OutboundV1Channel<SP> where SP::Target: SignerProvider {
95369599 is_v2_established: false,
95379600 holder_commitment_point,
95389601 #[cfg(splicing)]
9539- pending_splice : None,
9602+ pending_splice_pre : None,
95409603 };
95419604
95429605 let need_channel_ready = channel.check_get_channel_ready(0, logger).is_some()
@@ -9812,7 +9875,7 @@ impl<SP: Deref> InboundV1Channel<SP> where SP::Target: SignerProvider {
98129875 is_v2_established: false,
98139876 holder_commitment_point,
98149877 #[cfg(splicing)]
9815- pending_splice : None,
9878+ pending_splice_pre : None,
98169879 };
98179880 let need_channel_ready = channel.check_get_channel_ready(0, logger).is_some()
98189881 || channel.context.signer_pending_channel_ready;
@@ -11196,7 +11259,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, &'c Channel
1119611259 is_v2_established,
1119711260 holder_commitment_point,
1119811261 #[cfg(splicing)]
11199- pending_splice : None,
11262+ pending_splice_pre : None,
1120011263 })
1120111264 }
1120211265}
@@ -13118,4 +13181,69 @@ mod tests {
1311813181 );
1311913182 }
1312013183 }
13184+
13185+ #[cfg(all(test, splicing))]
13186+ fn get_pre_and_post(pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64) -> (u64, u64) {
13187+ use crate::ln::channel::PendingSplice;
13188+
13189+ let post_channel_value = PendingSplice::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution);
13190+ (pre_channel_value, post_channel_value)
13191+ }
13192+
13193+ #[cfg(all(test, splicing))]
13194+ #[test]
13195+ fn test_splice_compute_post_value() {
13196+ {
13197+ // increase, small amounts
13198+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 6_000, 0);
13199+ assert_eq!(pre_channel_value, 9_000);
13200+ assert_eq!(post_channel_value, 15_000);
13201+ }
13202+ {
13203+ // increase, small amounts
13204+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 4_000, 2_000);
13205+ assert_eq!(pre_channel_value, 9_000);
13206+ assert_eq!(post_channel_value, 15_000);
13207+ }
13208+ {
13209+ // increase, small amounts
13210+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 0, 6_000);
13211+ assert_eq!(pre_channel_value, 9_000);
13212+ assert_eq!(post_channel_value, 15_000);
13213+ }
13214+ {
13215+ // decrease, small amounts
13216+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -6_000, 0);
13217+ assert_eq!(pre_channel_value, 15_000);
13218+ assert_eq!(post_channel_value, 9_000);
13219+ }
13220+ {
13221+ // decrease, small amounts
13222+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -4_000, -2_000);
13223+ assert_eq!(pre_channel_value, 15_000);
13224+ assert_eq!(post_channel_value, 9_000);
13225+ }
13226+ {
13227+ // increase and decrease
13228+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, 4_000, -2_000);
13229+ assert_eq!(pre_channel_value, 15_000);
13230+ assert_eq!(post_channel_value, 17_000);
13231+ }
13232+ let base2: u64 = 2;
13233+ let huge63i3 = (base2.pow(63) - 3) as i64;
13234+ assert_eq!(huge63i3, 9223372036854775805);
13235+ assert_eq!(-huge63i3, -9223372036854775805);
13236+ {
13237+ // increase, large amount
13238+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, 3);
13239+ assert_eq!(pre_channel_value, 9_000);
13240+ assert_eq!(post_channel_value, 9223372036854784807);
13241+ }
13242+ {
13243+ // increase, large amounts
13244+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, huge63i3);
13245+ assert_eq!(pre_channel_value, 9_000);
13246+ assert_eq!(post_channel_value, 9223372036854784807);
13247+ }
13248+ }
1312113249}
0 commit comments