@@ -2242,6 +2242,23 @@ impl<'a> From<&'a Transaction> for ConfirmedTransaction<'a> {
22422242 }
22432243}
22442244
2245+ #[cfg(splicing)]
2246+ impl PendingSplice {
2247+ #[inline]
2248+ fn add_checked(base: u64, delta: i64) -> u64 {
2249+ if delta >= 0 {
2250+ base.saturating_add(delta as u64)
2251+ } else {
2252+ base.saturating_sub(delta.abs() as u64)
2253+ }
2254+ }
2255+
2256+ /// Compute the post-splice channel value from the pre-splice values and the peer contributions
2257+ pub fn compute_post_value(pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64) -> u64 {
2258+ Self::add_checked(pre_channel_value, our_funding_contribution.saturating_add(their_funding_contribution))
2259+ }
2260+ }
2261+
22452262/// Contains everything about the channel including state, and various flags.
22462263pub(super) struct ChannelContext<SP: Deref>
22472264where
@@ -5098,6 +5115,59 @@ where
50985115 }
50995116 }
51005117
5118+ /// Check that post-splicing balance meets reserve requirements, but only if it met it pre-splice as well
5119+ #[cfg(splicing)]
5120+ pub fn check_splice_balance_meets_v2_reserve_requirement_noerr(pre_balance: u64, post_balance: u64, pre_channel_value: u64, post_channel_value: u64, dust_limit: u64) -> Result<(), u64> {
5121+ if post_balance == 0 {
5122+ // 0 balance is fine
5123+ return Ok(());
5124+ }
5125+ let post_channel_reserve = get_v2_channel_reserve_satoshis(post_channel_value, dust_limit);
5126+ if post_balance >= post_channel_reserve {
5127+ return Ok(());
5128+ }
5129+ // post is not OK, check pre
5130+ if pre_balance == 0 {
5131+ // pre OK, post not -> not
5132+ return Err(post_channel_reserve);
5133+ }
5134+ let pre_channel_reserve = get_v2_channel_reserve_satoshis(pre_channel_value, dust_limit);
5135+ if pre_balance >= pre_channel_reserve {
5136+ // pre OK, post not -> not
5137+ return Err(post_channel_reserve);
5138+ }
5139+ // post not OK, but so was pre -> OK
5140+ Ok(())
5141+ }
5142+
5143+ /// Check that balances meet the channel reserve requirements or violates them (below reserve).
5144+ /// The channel value is an input as opposed to using from the FundingScope, so that this can be used in case of splicing
5145+ /// to check with new channel value (before being committed to it).
5146+ #[cfg(splicing)]
5147+ pub fn check_splice_balances_meet_v2_reserve_requirements(&self, self_balance_pre: u64, self_balance_post: u64, counterparty_balance_pre: u64, counterparty_balance_post: u64, channel_value_pre: u64, channel_value_post: u64) -> Result<(), ChannelError> {
5148+ let is_ok_self = Self::check_splice_balance_meets_v2_reserve_requirement_noerr(
5149+ self_balance_pre, self_balance_post, channel_value_pre, channel_value_post,
5150+ self.holder_dust_limit_satoshis
5151+ );
5152+ if let Err(channel_reserve_self) = is_ok_self {
5153+ return Err(ChannelError::Warn(format!(
5154+ "Balance below reserve, mandated by holder, {} vs {}",
5155+ self_balance_post, channel_reserve_self,
5156+ )));
5157+ }
5158+ let is_ok_cp = Self::check_splice_balance_meets_v2_reserve_requirement_noerr(
5159+ counterparty_balance_pre, counterparty_balance_post, channel_value_pre, channel_value_post,
5160+ self.counterparty_dust_limit_satoshis
5161+ );
5162+ if let Err(channel_reserve_cp) = is_ok_cp {
5163+ return Err(ChannelError::Warn(format!(
5164+ "Balance below reserve mandated by counterparty, {} vs {}",
5165+ counterparty_balance_post, channel_reserve_cp,
5166+ )));
5167+ }
5168+ Ok(())
5169+ }
5170+
51015171 /// Get the commitment tx fee for the local's (i.e. our) next commitment transaction based on the
51025172 /// number of pending HTLCs that are on track to be in our next commitment tx.
51035173 ///
@@ -10334,14 +10404,28 @@ where
1033410404
1033510405 /// Handle splice_ack
1033610406 #[cfg(splicing)]
10337- pub fn splice_ack(&mut self, _msg : &msgs::SpliceAck) -> Result<(), ChannelError> {
10407+ pub fn splice_ack(&mut self, msg : &msgs::SpliceAck) -> Result<(), ChannelError> {
1033810408 // check if splice is pending
10339- if self.pending_splice.is_none() {
10409+ let pending_splice = if let Some(pending_splice) = &self.pending_splice {
10410+ pending_splice
10411+ } else {
1034010412 return Err(ChannelError::Warn(format!("Channel is not in pending splice")));
1034110413 };
1034210414
10343- // TODO(splicing): Pre-check for reserve requirement
10415+ // Pre-check for reserve requirement
1034410416 // (Note: It should also be checked later at tx_complete)
10417+ let our_funding_contribution = pending_splice.our_funding_contribution;
10418+ let their_funding_contribution_satoshis = msg.funding_contribution_satoshis;
10419+
10420+ let pre_channel_value = self.funding.get_value_satoshis();
10421+ let post_channel_value = PendingSplice::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution_satoshis);
10422+ let pre_balance_self = self.funding.value_to_self_msat;
10423+ let post_balance_self = PendingSplice::add_checked(pre_balance_self, our_funding_contribution);
10424+ let pre_balance_counterparty = pre_channel_value.saturating_sub(pre_balance_self);
10425+ let post_balance_counterparty = post_channel_value.saturating_sub(post_balance_self);
10426+ // Pre-check for reserve requirement
10427+ // This will also be checked later at tx_complete
10428+ let _res = self.context.check_splice_balances_meet_v2_reserve_requirements(pre_balance_self, post_balance_self, pre_balance_counterparty, post_balance_counterparty, pre_channel_value, post_channel_value)?;
1034510429 Ok(())
1034610430 }
1034710431
@@ -15106,4 +15190,69 @@ mod tests {
1510615190 );
1510715191 }
1510815192 }
15193+
15194+ #[cfg(splicing)]
15195+ fn get_pre_and_post(pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64) -> (u64, u64) {
15196+ use crate::ln::channel::PendingSplice;
15197+
15198+ let post_channel_value = PendingSplice::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution);
15199+ (pre_channel_value, post_channel_value)
15200+ }
15201+
15202+ #[cfg(splicing)]
15203+ #[test]
15204+ fn test_splice_compute_post_value() {
15205+ {
15206+ // increase, small amounts
15207+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 6_000, 0);
15208+ assert_eq!(pre_channel_value, 9_000);
15209+ assert_eq!(post_channel_value, 15_000);
15210+ }
15211+ {
15212+ // increase, small amounts
15213+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 4_000, 2_000);
15214+ assert_eq!(pre_channel_value, 9_000);
15215+ assert_eq!(post_channel_value, 15_000);
15216+ }
15217+ {
15218+ // increase, small amounts
15219+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 0, 6_000);
15220+ assert_eq!(pre_channel_value, 9_000);
15221+ assert_eq!(post_channel_value, 15_000);
15222+ }
15223+ {
15224+ // decrease, small amounts
15225+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -6_000, 0);
15226+ assert_eq!(pre_channel_value, 15_000);
15227+ assert_eq!(post_channel_value, 9_000);
15228+ }
15229+ {
15230+ // decrease, small amounts
15231+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -4_000, -2_000);
15232+ assert_eq!(pre_channel_value, 15_000);
15233+ assert_eq!(post_channel_value, 9_000);
15234+ }
15235+ {
15236+ // increase and decrease
15237+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, 4_000, -2_000);
15238+ assert_eq!(pre_channel_value, 15_000);
15239+ assert_eq!(post_channel_value, 17_000);
15240+ }
15241+ let base2: u64 = 2;
15242+ let huge63i3 = (base2.pow(63) - 3) as i64;
15243+ assert_eq!(huge63i3, 9223372036854775805);
15244+ assert_eq!(-huge63i3, -9223372036854775805);
15245+ {
15246+ // increase, large amount
15247+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, 3);
15248+ assert_eq!(pre_channel_value, 9_000);
15249+ assert_eq!(post_channel_value, 9223372036854784807);
15250+ }
15251+ {
15252+ // increase, large amounts
15253+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, huge63i3);
15254+ assert_eq!(pre_channel_value, 9_000);
15255+ assert_eq!(post_channel_value, 9223372036854784807);
15256+ }
15257+ }
1510915258}
0 commit comments