@@ -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,
@@ -4249,6 +4266,59 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
42494266 }
42504267 }
42514268
4269+ /// Check that post-splicing balance meets reserve requirements, but only if it met it pre-splice as well
4270+ #[cfg(splicing)]
4271+ 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> {
4272+ if post_balance == 0 {
4273+ // 0 balance is fine
4274+ return Ok(());
4275+ }
4276+ let post_channel_reserve = get_v2_channel_reserve_satoshis(post_channel_value, dust_limit);
4277+ if post_balance >= post_channel_reserve {
4278+ return Ok(());
4279+ }
4280+ // post is not OK, check pre
4281+ if pre_balance == 0 {
4282+ // pre OK, post not -> not
4283+ return Err(post_channel_reserve);
4284+ }
4285+ let pre_channel_reserve = get_v2_channel_reserve_satoshis(pre_channel_value, dust_limit);
4286+ if pre_balance >= pre_channel_reserve {
4287+ // pre OK, post not -> not
4288+ return Err(post_channel_reserve);
4289+ }
4290+ // post not OK, but so was pre -> OK
4291+ Ok(())
4292+ }
4293+
4294+ /// Check that balances meet the channel reserve requirements or violates them (below reserve).
4295+ /// The channel value is an input as opposed to using from the FundingScope, so that this can be used in case of splicing
4296+ /// to check with new channel value (before being committed to it).
4297+ #[cfg(splicing)]
4298+ 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> {
4299+ let is_ok_self = Self::check_splice_balance_meets_v2_reserve_requirement_noerr(
4300+ self_balance_pre, self_balance_post, channel_value_pre, channel_value_post,
4301+ self.holder_dust_limit_satoshis
4302+ );
4303+ if let Err(channel_reserve_self) = is_ok_self {
4304+ return Err(ChannelError::Warn(format!(
4305+ "Balance below reserve, mandated by holder, {} vs {}",
4306+ self_balance_post, channel_reserve_self,
4307+ )));
4308+ }
4309+ let is_ok_cp = Self::check_splice_balance_meets_v2_reserve_requirement_noerr(
4310+ counterparty_balance_pre, counterparty_balance_post, channel_value_pre, channel_value_post,
4311+ self.counterparty_dust_limit_satoshis
4312+ );
4313+ if let Err(channel_reserve_cp) = is_ok_cp {
4314+ return Err(ChannelError::Warn(format!(
4315+ "Balance below reserve mandated by counterparty, {} vs {}",
4316+ counterparty_balance_post, channel_reserve_cp,
4317+ )));
4318+ }
4319+ Ok(())
4320+ }
4321+
42524322 /// Get the commitment tx fee for the local's (i.e. our) next commitment transaction based on the
42534323 /// number of pending HTLCs that are on track to be in our next commitment tx.
42544324 ///
@@ -8542,14 +8612,28 @@ impl<SP: Deref> FundedChannel<SP> where
85428612
85438613 /// Handle splice_ack
85448614 #[cfg(splicing)]
8545- pub fn splice_ack(&mut self, _msg : &msgs::SpliceAck) -> Result<(), ChannelError> {
8615+ pub fn splice_ack(&mut self, msg : &msgs::SpliceAck) -> Result<(), ChannelError> {
85468616 // check if splice is pending
8547- if self.pending_splice.is_none() {
8617+ let pending_splice = if let Some(pending_splice) = &self.pending_splice {
8618+ pending_splice
8619+ } else {
85488620 return Err(ChannelError::Warn(format!("Channel is not in pending splice")));
85498621 };
85508622
8551- // TODO(splicing): Pre-check for reserve requirement
8623+ // Pre-check for reserve requirement
85528624 // (Note: It should also be checked later at tx_complete)
8625+ let our_funding_contribution = pending_splice.our_funding_contribution;
8626+ let their_funding_contribution_satoshis = msg.funding_contribution_satoshis;
8627+
8628+ let pre_channel_value = self.funding.get_value_satoshis();
8629+ let post_channel_value = PendingSplice::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution_satoshis);
8630+ let pre_balance_self = self.funding.value_to_self_msat;
8631+ let post_balance_self = PendingSplice::add_checked(pre_balance_self, our_funding_contribution);
8632+ let pre_balance_counterparty = pre_channel_value.saturating_sub(pre_balance_self);
8633+ let post_balance_counterparty = post_channel_value.saturating_sub(post_balance_self);
8634+ // Pre-check for reserve requirement
8635+ // This will also be checked later at tx_complete
8636+ 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)?;
85538637 Ok(())
85548638 }
85558639
@@ -13061,4 +13145,69 @@ mod tests {
1306113145 );
1306213146 }
1306313147 }
13148+
13149+ #[cfg(splicing)]
13150+ fn get_pre_and_post(pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64) -> (u64, u64) {
13151+ use crate::ln::channel::PendingSplice;
13152+
13153+ let post_channel_value = PendingSplice::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution);
13154+ (pre_channel_value, post_channel_value)
13155+ }
13156+
13157+ #[cfg(splicing)]
13158+ #[test]
13159+ fn test_splice_compute_post_value() {
13160+ {
13161+ // increase, small amounts
13162+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 6_000, 0);
13163+ assert_eq!(pre_channel_value, 9_000);
13164+ assert_eq!(post_channel_value, 15_000);
13165+ }
13166+ {
13167+ // increase, small amounts
13168+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 4_000, 2_000);
13169+ assert_eq!(pre_channel_value, 9_000);
13170+ assert_eq!(post_channel_value, 15_000);
13171+ }
13172+ {
13173+ // increase, small amounts
13174+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 0, 6_000);
13175+ assert_eq!(pre_channel_value, 9_000);
13176+ assert_eq!(post_channel_value, 15_000);
13177+ }
13178+ {
13179+ // decrease, small amounts
13180+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -6_000, 0);
13181+ assert_eq!(pre_channel_value, 15_000);
13182+ assert_eq!(post_channel_value, 9_000);
13183+ }
13184+ {
13185+ // decrease, small amounts
13186+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -4_000, -2_000);
13187+ assert_eq!(pre_channel_value, 15_000);
13188+ assert_eq!(post_channel_value, 9_000);
13189+ }
13190+ {
13191+ // increase and decrease
13192+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, 4_000, -2_000);
13193+ assert_eq!(pre_channel_value, 15_000);
13194+ assert_eq!(post_channel_value, 17_000);
13195+ }
13196+ let base2: u64 = 2;
13197+ let huge63i3 = (base2.pow(63) - 3) as i64;
13198+ assert_eq!(huge63i3, 9223372036854775805);
13199+ assert_eq!(-huge63i3, -9223372036854775805);
13200+ {
13201+ // increase, large amount
13202+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, 3);
13203+ assert_eq!(pre_channel_value, 9_000);
13204+ assert_eq!(post_channel_value, 9223372036854784807);
13205+ }
13206+ {
13207+ // increase, large amounts
13208+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, huge63i3);
13209+ assert_eq!(pre_channel_value, 9_000);
13210+ assert_eq!(post_channel_value, 9223372036854784807);
13211+ }
13212+ }
1306413213}
0 commit comments