@@ -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))
@@ -1731,6 +1731,23 @@ struct PendingSplice {
17311731 pub our_funding_contribution: i64,
17321732}
17331733
1734+ #[cfg(splicing)]
1735+ impl PendingSplice {
1736+ #[inline]
1737+ fn add_checked(base: u64, delta: i64) -> u64 {
1738+ if delta >= 0 {
1739+ base.saturating_add(delta as u64)
1740+ } else {
1741+ base.saturating_sub(delta.abs() as u64)
1742+ }
1743+ }
1744+
1745+ /// Compute the post-splice channel value from the pre-splice values and the peer contributions
1746+ pub fn compute_post_value(pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64) -> u64 {
1747+ Self::add_checked(pre_channel_value, our_funding_contribution.saturating_add(their_funding_contribution))
1748+ }
1749+ }
1750+
17341751/// Contains everything about the channel including state, and various flags.
17351752pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
17361753 config: LegacyChannelConfig,
@@ -4213,6 +4230,33 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
42134230 }
42144231 }
42154232
4233+ /// Check that a balance value meets the channel reserve requirements or violates them (below reserve).
4234+ /// The channel value is an input as opposed to using from self, so that this can be used in case of splicing
4235+ /// to checks with new channel value (before being comitted to it).
4236+ #[cfg(splicing)]
4237+ pub fn check_balance_meets_v2_reserve_requirements(&self, balance: u64, channel_value: u64) -> Result<(), ChannelError> {
4238+ if balance == 0 {
4239+ return Ok(());
4240+ }
4241+ let holder_selected_channel_reserve_satoshis = get_v2_channel_reserve_satoshis(
4242+ channel_value, self.holder_dust_limit_satoshis);
4243+ if balance < holder_selected_channel_reserve_satoshis {
4244+ return Err(ChannelError::Warn(format!(
4245+ "Balance below reserve mandated by holder, {} vs {}",
4246+ balance, holder_selected_channel_reserve_satoshis,
4247+ )));
4248+ }
4249+ let counterparty_selected_channel_reserve_satoshis = get_v2_channel_reserve_satoshis(
4250+ channel_value, self.counterparty_dust_limit_satoshis);
4251+ if balance < counterparty_selected_channel_reserve_satoshis {
4252+ return Err(ChannelError::Warn(format!(
4253+ "Balance below reserve mandated by counterparty, {} vs {}",
4254+ balance, counterparty_selected_channel_reserve_satoshis,
4255+ )));
4256+ }
4257+ Ok(())
4258+ }
4259+
42164260 /// Get the commitment tx fee for the local's (i.e. our) next commitment transaction based on the
42174261 /// number of pending HTLCs that are on track to be in our next commitment tx.
42184262 ///
@@ -4860,7 +4904,7 @@ pub(super) struct FundedChannel<SP: Deref> where SP::Target: SignerProvider {
48604904 is_v2_established: bool,
48614905 /// Info about an in-progress, pending splice (if any), on the pre-splice channel
48624906 #[cfg(splicing)]
4863- pending_splice : Option<PendingSplice>,
4907+ pending_splice_pre : Option<PendingSplice>,
48644908}
48654909
48664910#[cfg(any(test, fuzzing))]
@@ -8395,7 +8439,7 @@ impl<SP: Deref> FundedChannel<SP> where
83958439 ) -> Result<msgs::SpliceInit, APIError> {
83968440 // Check if a splice has been initiated already.
83978441 // Note: only a single outstanding splice is supported (per spec)
8398- if let Some(splice_info) = &self.pending_splice {
8442+ if let Some(splice_info) = &self.pending_splice_pre {
83998443 return Err(APIError::APIMisuseError { err: format!(
84008444 "Channel {} cannot be spliced, as it has already a splice pending (contribution {})",
84018445 self.context.channel_id(), splice_info.our_funding_contribution
@@ -8431,7 +8475,7 @@ impl<SP: Deref> FundedChannel<SP> where
84318475 self.context.channel_id(), err,
84328476 )})?;
84338477
8434- self.pending_splice = Some(PendingSplice {
8478+ self.pending_splice_pre = Some(PendingSplice {
84358479 our_funding_contribution: our_funding_contribution_satoshis,
84368480 });
84378481
@@ -8465,7 +8509,7 @@ impl<SP: Deref> FundedChannel<SP> where
84658509 let our_funding_contribution_satoshis = 0i64;
84668510
84678511 // Check if a splice has been initiated already.
8468- if let Some(splice_info) = &self.pending_splice {
8512+ if let Some(splice_info) = &self.pending_splice_pre {
84698513 return Err(ChannelError::Warn(format!(
84708514 "Channel has already a splice pending, contribution {}", splice_info.our_funding_contribution,
84718515 )));
@@ -8491,7 +8535,13 @@ impl<SP: Deref> FundedChannel<SP> where
84918535
84928536 // Note on channel reserve requirement pre-check: as the splice acceptor does not contribute,
84938537 // it can't go below reserve, therefore no pre-check is done here.
8494- // TODO(splicing): Once splice acceptor can contribute, add reserve pre-check, similar to the one in `splice_ack`.
8538+
8539+ let pre_channel_value = self.funding.value_to_self_msat;
8540+ let post_channel_value = PendingSplice::compute_post_value(pre_channel_value, their_funding_contribution_satoshis, our_funding_contribution_satoshis);
8541+ let post_balance = PendingSplice::add_checked(self.funding.value_to_self_msat, our_funding_contribution_satoshis);
8542+ // Early check for reserve requirement, assuming maximum balance of full channel value
8543+ // This will also be checked later at tx_complete
8544+ let _res = self.context.check_balance_meets_v2_reserve_requirements(post_balance, post_channel_value)?;
84958545
84968546 // TODO(splicing): Store msg.funding_pubkey
84978547 // TODO(splicing): Apply start of splice (splice_start)
@@ -8510,14 +8560,27 @@ impl<SP: Deref> FundedChannel<SP> where
85108560
85118561 /// Handle splice_ack
85128562 #[cfg(splicing)]
8513- pub fn splice_ack(&mut self, _msg : &msgs::SpliceAck) -> Result<(), ChannelError> {
8563+ pub fn splice_ack(&mut self, msg : &msgs::SpliceAck) -> Result<(), ChannelError> {
85148564 // check if splice is pending
8515- if self.pending_splice.is_none() {
8565+ let pending_splice = if let Some(pending_splice) = &self.pending_splice_pre {
8566+ pending_splice
8567+ } else {
85168568 return Err(ChannelError::Warn(format!("Channel is not in pending splice")));
85178569 };
85188570
85198571 // TODO(splicing): Pre-check for reserve requirement
85208572 // (Note: It should also be checked later at tx_complete)
8573+
8574+
8575+ let our_funding_contribution = pending_splice.our_funding_contribution;
8576+ let their_funding_contribution_satoshis = msg.funding_contribution_satoshis;
8577+
8578+ let pre_channel_value = self.funding.get_value_satoshis();
8579+ let post_channel_value = PendingSplice::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution_satoshis);
8580+ let post_balance = PendingSplice::add_checked(self.funding.value_to_self_msat, our_funding_contribution);
8581+ // Early check for reserve requirement, assuming maximum balance of full channel value
8582+ // This will also be checked later at tx_complete
8583+ let _res = self.context.check_balance_meets_v2_reserve_requirements(post_balance, post_channel_value)?;
85218584 Ok(())
85228585 }
85238586
@@ -9444,7 +9507,7 @@ impl<SP: Deref> OutboundV1Channel<SP> where SP::Target: SignerProvider {
94449507 is_v2_established: false,
94459508 holder_commitment_point,
94469509 #[cfg(splicing)]
9447- pending_splice : None,
9510+ pending_splice_pre : None,
94489511 };
94499512
94509513 let need_channel_ready = channel.check_get_channel_ready(0, logger).is_some()
@@ -9720,7 +9783,7 @@ impl<SP: Deref> InboundV1Channel<SP> where SP::Target: SignerProvider {
97209783 is_v2_established: false,
97219784 holder_commitment_point,
97229785 #[cfg(splicing)]
9723- pending_splice : None,
9786+ pending_splice_pre : None,
97249787 };
97259788 let need_channel_ready = channel.check_get_channel_ready(0, logger).is_some()
97269789 || channel.context.signer_pending_channel_ready;
@@ -11101,7 +11164,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, &'c Channel
1110111164 is_v2_established,
1110211165 holder_commitment_point,
1110311166 #[cfg(splicing)]
11104- pending_splice : None,
11167+ pending_splice_pre : None,
1110511168 })
1110611169 }
1110711170}
@@ -13023,4 +13086,69 @@ mod tests {
1302313086 );
1302413087 }
1302513088 }
13089+
13090+ #[cfg(all(test, splicing))]
13091+ fn get_pre_and_post(pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64) -> (u64, u64) {
13092+ use crate::ln::channel::PendingSplice;
13093+
13094+ let post_channel_value = PendingSplice::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution);
13095+ (pre_channel_value, post_channel_value)
13096+ }
13097+
13098+ #[cfg(all(test, splicing))]
13099+ #[test]
13100+ fn test_splice_compute_post_value() {
13101+ {
13102+ // increase, small amounts
13103+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 6_000, 0);
13104+ assert_eq!(pre_channel_value, 9_000);
13105+ assert_eq!(post_channel_value, 15_000);
13106+ }
13107+ {
13108+ // increase, small amounts
13109+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 4_000, 2_000);
13110+ assert_eq!(pre_channel_value, 9_000);
13111+ assert_eq!(post_channel_value, 15_000);
13112+ }
13113+ {
13114+ // increase, small amounts
13115+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 0, 6_000);
13116+ assert_eq!(pre_channel_value, 9_000);
13117+ assert_eq!(post_channel_value, 15_000);
13118+ }
13119+ {
13120+ // decrease, small amounts
13121+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -6_000, 0);
13122+ assert_eq!(pre_channel_value, 15_000);
13123+ assert_eq!(post_channel_value, 9_000);
13124+ }
13125+ {
13126+ // decrease, small amounts
13127+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -4_000, -2_000);
13128+ assert_eq!(pre_channel_value, 15_000);
13129+ assert_eq!(post_channel_value, 9_000);
13130+ }
13131+ {
13132+ // increase and decrease
13133+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, 4_000, -2_000);
13134+ assert_eq!(pre_channel_value, 15_000);
13135+ assert_eq!(post_channel_value, 17_000);
13136+ }
13137+ let base2: u64 = 2;
13138+ let huge63i3 = (base2.pow(63) - 3) as i64;
13139+ assert_eq!(huge63i3, 9223372036854775805);
13140+ assert_eq!(-huge63i3, -9223372036854775805);
13141+ {
13142+ // increase, large amount
13143+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, 3);
13144+ assert_eq!(pre_channel_value, 9_000);
13145+ assert_eq!(post_channel_value, 9223372036854784807);
13146+ }
13147+ {
13148+ // increase, large amounts
13149+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, huge63i3);
13150+ assert_eq!(pre_channel_value, 9_000);
13151+ assert_eq!(post_channel_value, 9223372036854784807);
13152+ }
13153+ }
1302613154}
0 commit comments