@@ -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,
@@ -4256,6 +4273,59 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
42564273 }
42574274 }
42584275
4276+ /// Check that post-splicing balance meets reserve requirements, but only if it met it pre-splice as well
4277+ #[cfg(splicing)]
4278+ 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> {
4279+ if post_balance == 0 {
4280+ // 0 balance is fine
4281+ return Ok(());
4282+ }
4283+ let post_channel_reserve = get_v2_channel_reserve_satoshis(post_channel_value, dust_limit);
4284+ if post_balance >= post_channel_reserve {
4285+ return Ok(());
4286+ }
4287+ // post is not OK, check pre
4288+ if pre_balance == 0 {
4289+ // pre OK, post not -> not
4290+ return Err(post_channel_reserve);
4291+ }
4292+ let pre_channel_reserve = get_v2_channel_reserve_satoshis(pre_channel_value, dust_limit);
4293+ if pre_balance >= pre_channel_reserve {
4294+ // pre OK, post not -> not
4295+ return Err(post_channel_reserve);
4296+ }
4297+ // post not OK, but so was pre -> OK
4298+ Ok(())
4299+ }
4300+
4301+ /// Check that balances meet the channel reserve requirements or violates them (below reserve).
4302+ /// The channel value is an input as opposed to using from the FundingScope, so that this can be used in case of splicing
4303+ /// to check with new channel value (before being committed to it).
4304+ #[cfg(splicing)]
4305+ 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> {
4306+ let is_ok_self = Self::check_splice_balance_meets_v2_reserve_requirement_noerr(
4307+ self_balance_pre, self_balance_post, channel_value_pre, channel_value_post,
4308+ self.holder_dust_limit_satoshis
4309+ );
4310+ if let Err(channel_reserve_self) = is_ok_self {
4311+ return Err(ChannelError::Warn(format!(
4312+ "Balance below reserve, mandated by holder, {} vs {}",
4313+ self_balance_post, channel_reserve_self,
4314+ )));
4315+ }
4316+ let is_ok_cp = Self::check_splice_balance_meets_v2_reserve_requirement_noerr(
4317+ counterparty_balance_pre, counterparty_balance_post, channel_value_pre, channel_value_post,
4318+ self.counterparty_dust_limit_satoshis
4319+ );
4320+ if let Err(channel_reserve_cp) = is_ok_cp {
4321+ return Err(ChannelError::Warn(format!(
4322+ "Balance below reserve mandated by counterparty, {} vs {}",
4323+ counterparty_balance_post, channel_reserve_cp,
4324+ )));
4325+ }
4326+ Ok(())
4327+ }
4328+
42594329 /// Get the commitment tx fee for the local's (i.e. our) next commitment transaction based on the
42604330 /// number of pending HTLCs that are on track to be in our next commitment tx.
42614331 ///
@@ -8550,14 +8620,28 @@ impl<SP: Deref> FundedChannel<SP> where
85508620
85518621 /// Handle splice_ack
85528622 #[cfg(splicing)]
8553- pub fn splice_ack(&mut self, _msg : &msgs::SpliceAck) -> Result<(), ChannelError> {
8623+ pub fn splice_ack(&mut self, msg : &msgs::SpliceAck) -> Result<(), ChannelError> {
85548624 // check if splice is pending
8555- if self.pending_splice.is_none() {
8625+ let pending_splice = if let Some(pending_splice) = &self.pending_splice {
8626+ pending_splice
8627+ } else {
85568628 return Err(ChannelError::Warn(format!("Channel is not in pending splice")));
85578629 };
85588630
8559- // TODO(splicing): Pre-check for reserve requirement
8631+ // Pre-check for reserve requirement
85608632 // (Note: It should also be checked later at tx_complete)
8633+ let our_funding_contribution = pending_splice.our_funding_contribution;
8634+ let their_funding_contribution_satoshis = msg.funding_contribution_satoshis;
8635+
8636+ let pre_channel_value = self.funding.get_value_satoshis();
8637+ let post_channel_value = PendingSplice::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution_satoshis);
8638+ let pre_balance_self = self.funding.value_to_self_msat;
8639+ let post_balance_self = PendingSplice::add_checked(pre_balance_self, our_funding_contribution);
8640+ let pre_balance_counterparty = pre_channel_value.saturating_sub(pre_balance_self);
8641+ let post_balance_counterparty = post_channel_value.saturating_sub(post_balance_self);
8642+ // Pre-check for reserve requirement
8643+ // This will also be checked later at tx_complete
8644+ 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)?;
85618645 Ok(())
85628646 }
85638647
@@ -13050,4 +13134,69 @@ mod tests {
1305013134 );
1305113135 }
1305213136 }
13137+
13138+ #[cfg(splicing)]
13139+ fn get_pre_and_post(pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64) -> (u64, u64) {
13140+ use crate::ln::channel::PendingSplice;
13141+
13142+ let post_channel_value = PendingSplice::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution);
13143+ (pre_channel_value, post_channel_value)
13144+ }
13145+
13146+ #[cfg(splicing)]
13147+ #[test]
13148+ fn test_splice_compute_post_value() {
13149+ {
13150+ // increase, small amounts
13151+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 6_000, 0);
13152+ assert_eq!(pre_channel_value, 9_000);
13153+ assert_eq!(post_channel_value, 15_000);
13154+ }
13155+ {
13156+ // increase, small amounts
13157+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 4_000, 2_000);
13158+ assert_eq!(pre_channel_value, 9_000);
13159+ assert_eq!(post_channel_value, 15_000);
13160+ }
13161+ {
13162+ // increase, small amounts
13163+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 0, 6_000);
13164+ assert_eq!(pre_channel_value, 9_000);
13165+ assert_eq!(post_channel_value, 15_000);
13166+ }
13167+ {
13168+ // decrease, small amounts
13169+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -6_000, 0);
13170+ assert_eq!(pre_channel_value, 15_000);
13171+ assert_eq!(post_channel_value, 9_000);
13172+ }
13173+ {
13174+ // decrease, small amounts
13175+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -4_000, -2_000);
13176+ assert_eq!(pre_channel_value, 15_000);
13177+ assert_eq!(post_channel_value, 9_000);
13178+ }
13179+ {
13180+ // increase and decrease
13181+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, 4_000, -2_000);
13182+ assert_eq!(pre_channel_value, 15_000);
13183+ assert_eq!(post_channel_value, 17_000);
13184+ }
13185+ let base2: u64 = 2;
13186+ let huge63i3 = (base2.pow(63) - 3) as i64;
13187+ assert_eq!(huge63i3, 9223372036854775805);
13188+ assert_eq!(-huge63i3, -9223372036854775805);
13189+ {
13190+ // increase, large amount
13191+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, 3);
13192+ assert_eq!(pre_channel_value, 9_000);
13193+ assert_eq!(post_channel_value, 9223372036854784807);
13194+ }
13195+ {
13196+ // increase, large amounts
13197+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, huge63i3);
13198+ assert_eq!(pre_channel_value, 9_000);
13199+ assert_eq!(post_channel_value, 9223372036854784807);
13200+ }
13201+ }
1305313202}
0 commit comments