@@ -2238,6 +2238,28 @@ impl<'a> From<&'a Transaction> for ConfirmedTransaction<'a> {
2238
2238
}
2239
2239
}
2240
2240
2241
+ #[cfg(splicing)]
2242
+ impl PendingSplice {
2243
+ #[inline]
2244
+ fn add_checked(base: u64, delta: i64) -> u64 {
2245
+ if delta >= 0 {
2246
+ base.saturating_add(delta as u64)
2247
+ } else {
2248
+ base.saturating_sub(delta.abs() as u64)
2249
+ }
2250
+ }
2251
+
2252
+ /// Compute the post-splice channel value from the pre-splice values and the peer contributions
2253
+ pub fn compute_post_value(
2254
+ pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64,
2255
+ ) -> u64 {
2256
+ Self::add_checked(
2257
+ pre_channel_value,
2258
+ our_funding_contribution.saturating_add(their_funding_contribution),
2259
+ )
2260
+ }
2261
+ }
2262
+
2241
2263
/// Contains everything about the channel including state, and various flags.
2242
2264
pub(super) struct ChannelContext<SP: Deref>
2243
2265
where
@@ -5051,6 +5073,71 @@ where
5051
5073
}
5052
5074
}
5053
5075
5076
+ /// Check that post-splicing balance meets reserve requirements, but only if it met it pre-splice as well
5077
+ #[cfg(splicing)]
5078
+ pub fn check_splice_balance_meets_v2_reserve_requirement_noerr(
5079
+ pre_balance: u64, post_balance: u64, pre_channel_value: u64, post_channel_value: u64,
5080
+ dust_limit: u64,
5081
+ ) -> Result<(), u64> {
5082
+ if post_balance == 0 {
5083
+ // 0 balance is fine
5084
+ return Ok(());
5085
+ }
5086
+ let post_channel_reserve = get_v2_channel_reserve_satoshis(post_channel_value, dust_limit);
5087
+ if post_balance >= post_channel_reserve {
5088
+ return Ok(());
5089
+ }
5090
+ // post is not OK, check pre
5091
+ if pre_balance == 0 {
5092
+ // pre OK, post not -> not
5093
+ return Err(post_channel_reserve);
5094
+ }
5095
+ let pre_channel_reserve = get_v2_channel_reserve_satoshis(pre_channel_value, dust_limit);
5096
+ if pre_balance >= pre_channel_reserve {
5097
+ // pre OK, post not -> not
5098
+ return Err(post_channel_reserve);
5099
+ }
5100
+ // post not OK, but so was pre -> OK
5101
+ Ok(())
5102
+ }
5103
+
5104
+ /// Check that balances meet the channel reserve requirements or violates them (below reserve).
5105
+ /// The channel value is an input as opposed to using from the FundingScope, so that this can be used in case of splicing
5106
+ /// to check with new channel value (before being committed to it).
5107
+ #[cfg(splicing)]
5108
+ pub fn check_splice_balances_meet_v2_reserve_requirements(
5109
+ &self, self_balance_pre: u64, self_balance_post: u64, counterparty_balance_pre: u64,
5110
+ counterparty_balance_post: u64, channel_value_pre: u64, channel_value_post: u64,
5111
+ ) -> Result<(), ChannelError> {
5112
+ let is_ok_self = Self::check_splice_balance_meets_v2_reserve_requirement_noerr(
5113
+ self_balance_pre,
5114
+ self_balance_post,
5115
+ channel_value_pre,
5116
+ channel_value_post,
5117
+ self.holder_dust_limit_satoshis,
5118
+ );
5119
+ if let Err(channel_reserve_self) = is_ok_self {
5120
+ return Err(ChannelError::Warn(format!(
5121
+ "Balance below reserve, mandated by holder, {} vs {}",
5122
+ self_balance_post, channel_reserve_self,
5123
+ )));
5124
+ }
5125
+ let is_ok_cp = Self::check_splice_balance_meets_v2_reserve_requirement_noerr(
5126
+ counterparty_balance_pre,
5127
+ counterparty_balance_post,
5128
+ channel_value_pre,
5129
+ channel_value_post,
5130
+ self.counterparty_dust_limit_satoshis,
5131
+ );
5132
+ if let Err(channel_reserve_cp) = is_ok_cp {
5133
+ return Err(ChannelError::Warn(format!(
5134
+ "Balance below reserve mandated by counterparty, {} vs {}",
5135
+ counterparty_balance_post, channel_reserve_cp,
5136
+ )));
5137
+ }
5138
+ Ok(())
5139
+ }
5140
+
5054
5141
/// Get the commitment tx fee for the local's (i.e. our) next commitment transaction based on the
5055
5142
/// number of pending HTLCs that are on track to be in our next commitment tx.
5056
5143
///
@@ -10517,14 +10604,28 @@ where
10517
10604
10518
10605
/// Handle splice_ack
10519
10606
#[cfg(splicing)]
10520
- pub fn splice_ack(&mut self, _msg : &msgs::SpliceAck) -> Result<(), ChannelError> {
10607
+ pub fn splice_ack(&mut self, msg : &msgs::SpliceAck) -> Result<(), ChannelError> {
10521
10608
// check if splice is pending
10522
- if self.pending_splice.is_none() {
10609
+ let pending_splice = if let Some(pending_splice) = &self.pending_splice {
10610
+ pending_splice
10611
+ } else {
10523
10612
return Err(ChannelError::Warn(format!("Channel is not in pending splice")));
10524
10613
};
10525
10614
10526
- // TODO(splicing): Pre-check for reserve requirement
10615
+ // Pre-check for reserve requirement
10527
10616
// (Note: It should also be checked later at tx_complete)
10617
+ let our_funding_contribution = pending_splice.our_funding_contribution;
10618
+ let their_funding_contribution_satoshis = msg.funding_contribution_satoshis;
10619
+
10620
+ let pre_channel_value = self.funding.get_value_satoshis();
10621
+ let post_channel_value = PendingSplice::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution_satoshis);
10622
+ let pre_balance_self = self.funding.value_to_self_msat;
10623
+ let post_balance_self = PendingSplice::add_checked(pre_balance_self, our_funding_contribution);
10624
+ let pre_balance_counterparty = pre_channel_value.saturating_sub(pre_balance_self);
10625
+ let post_balance_counterparty = post_channel_value.saturating_sub(post_balance_self);
10626
+ // Pre-check for reserve requirement
10627
+ // This will also be checked later at tx_complete
10628
+ 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)?;
10528
10629
Ok(())
10529
10630
}
10530
10631
@@ -15559,4 +15660,76 @@ mod tests {
15559
15660
);
15560
15661
}
15561
15662
}
15663
+
15664
+ #[cfg(splicing)]
15665
+ fn get_pre_and_post(
15666
+ pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64,
15667
+ ) -> (u64, u64) {
15668
+ use crate::ln::channel::PendingSplice;
15669
+
15670
+ let post_channel_value = PendingSplice::compute_post_value(
15671
+ pre_channel_value,
15672
+ our_funding_contribution,
15673
+ their_funding_contribution,
15674
+ );
15675
+ (pre_channel_value, post_channel_value)
15676
+ }
15677
+
15678
+ #[cfg(splicing)]
15679
+ #[test]
15680
+ fn test_splice_compute_post_value() {
15681
+ {
15682
+ // increase, small amounts
15683
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 6_000, 0);
15684
+ assert_eq!(pre_channel_value, 9_000);
15685
+ assert_eq!(post_channel_value, 15_000);
15686
+ }
15687
+ {
15688
+ // increase, small amounts
15689
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 4_000, 2_000);
15690
+ assert_eq!(pre_channel_value, 9_000);
15691
+ assert_eq!(post_channel_value, 15_000);
15692
+ }
15693
+ {
15694
+ // increase, small amounts
15695
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 0, 6_000);
15696
+ assert_eq!(pre_channel_value, 9_000);
15697
+ assert_eq!(post_channel_value, 15_000);
15698
+ }
15699
+ {
15700
+ // decrease, small amounts
15701
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -6_000, 0);
15702
+ assert_eq!(pre_channel_value, 15_000);
15703
+ assert_eq!(post_channel_value, 9_000);
15704
+ }
15705
+ {
15706
+ // decrease, small amounts
15707
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -4_000, -2_000);
15708
+ assert_eq!(pre_channel_value, 15_000);
15709
+ assert_eq!(post_channel_value, 9_000);
15710
+ }
15711
+ {
15712
+ // increase and decrease
15713
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, 4_000, -2_000);
15714
+ assert_eq!(pre_channel_value, 15_000);
15715
+ assert_eq!(post_channel_value, 17_000);
15716
+ }
15717
+ let base2: u64 = 2;
15718
+ let huge63i3 = (base2.pow(63) - 3) as i64;
15719
+ assert_eq!(huge63i3, 9223372036854775805);
15720
+ assert_eq!(-huge63i3, -9223372036854775805);
15721
+ {
15722
+ // increase, large amount
15723
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, 3);
15724
+ assert_eq!(pre_channel_value, 9_000);
15725
+ assert_eq!(post_channel_value, 9223372036854784807);
15726
+ }
15727
+ {
15728
+ // increase, large amounts
15729
+ let (pre_channel_value, post_channel_value) =
15730
+ get_pre_and_post(9_000, huge63i3, huge63i3);
15731
+ assert_eq!(pre_channel_value, 9_000);
15732
+ assert_eq!(post_channel_value, 9223372036854784807);
15733
+ }
15734
+ }
15562
15735
}
0 commit comments