@@ -2477,6 +2477,28 @@ impl<'a> From<&'a Transaction> for ConfirmedTransaction<'a> {
24772477 }
24782478}
24792479
2480+ #[cfg(splicing)]
2481+ impl PendingSplice {
2482+ #[inline]
2483+ fn add_checked(base: u64, delta: i64) -> u64 {
2484+ if delta >= 0 {
2485+ base.saturating_add(delta as u64)
2486+ } else {
2487+ base.saturating_sub(delta.abs() as u64)
2488+ }
2489+ }
2490+
2491+ // /// Compute the post-splice channel value from the pre-splice values and the peer contributions
2492+ // pub fn compute_post_splice_value(
2493+ // pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64,
2494+ // ) -> u64 {
2495+ // Self::add_checked(
2496+ // pre_channel_value,
2497+ // our_funding_contribution.saturating_add(their_funding_contribution),
2498+ // )
2499+ // }
2500+ }
2501+
24802502/// Contains everything about the channel including state, and various flags.
24812503pub(super) struct ChannelContext<SP: Deref>
24822504where
@@ -10839,35 +10861,37 @@ where
1083910861 ES::Target: EntropySource,
1084010862 L::Target: Logger,
1084110863 {
10842- let pending_splice = if let Some(ref mut pending_splice) = &mut self.pending_splice {
10843- pending_splice
10844- } else {
10845- return Err(ChannelError::Ignore(format!("Channel is not in pending splice")));
10846- };
10864+ let funding_negotiation_context = {
10865+ let pending_splice = if let Some(ref mut pending_splice) = &mut self.pending_splice {
10866+ pending_splice
10867+ } else {
10868+ return Err(ChannelError::Ignore(format!("Channel is not in pending splice")));
10869+ };
1084710870
10848- // TODO(splicing): Add check that we are the splice (quiescence) initiator
10871+ // TODO(splicing): Add check that we are the splice (quiescence) initiator
1084910872
10850- let funding_negotiation_context = match pending_splice.funding_negotiation.take() {
10851- Some(FundingNegotiation::AwaitingAck(context)) => context,
10852- Some(FundingNegotiation::ConstructingTransaction(funding, constructor)) => {
10853- pending_splice.funding_negotiation =
10854- Some(FundingNegotiation::ConstructingTransaction(funding, constructor));
10855- return Err(ChannelError::WarnAndDisconnect(format!(
10856- "Got unexpected splice_ack; splice negotiation already in progress"
10857- )));
10858- },
10859- Some(FundingNegotiation::AwaitingSignatures(funding)) => {
10860- pending_splice.funding_negotiation =
10861- Some(FundingNegotiation::AwaitingSignatures(funding));
10862- return Err(ChannelError::WarnAndDisconnect(format!(
10863- "Got unexpected splice_ack; splice negotiation already in progress"
10864- )));
10865- },
10866- None => {
10867- return Err(ChannelError::Ignore(format!(
10868- "Got unexpected splice_ack; no splice negotiation in progress"
10869- )));
10870- },
10873+ match pending_splice.funding_negotiation.take() {
10874+ Some(FundingNegotiation::AwaitingAck(context)) => context,
10875+ Some(FundingNegotiation::ConstructingTransaction(funding, constructor)) => {
10876+ pending_splice.funding_negotiation =
10877+ Some(FundingNegotiation::ConstructingTransaction(funding, constructor));
10878+ return Err(ChannelError::WarnAndDisconnect(format!(
10879+ "Got unexpected splice_ack; splice negotiation already in progress"
10880+ )));
10881+ },
10882+ Some(FundingNegotiation::AwaitingSignatures(funding)) => {
10883+ pending_splice.funding_negotiation =
10884+ Some(FundingNegotiation::AwaitingSignatures(funding));
10885+ return Err(ChannelError::WarnAndDisconnect(format!(
10886+ "Got unexpected splice_ack; splice negotiation already in progress"
10887+ )));
10888+ },
10889+ None => {
10890+ return Err(ChannelError::Ignore(format!(
10891+ "Got unexpected splice_ack; no splice negotiation in progress"
10892+ )));
10893+ },
10894+ }
1087110895 };
1087210896
1087310897 let our_funding_contribution_satoshis =
@@ -10882,8 +10906,28 @@ where
1088210906 msg.funding_pubkey,
1088310907 )?;
1088410908
10885- // TODO(splicing): Pre-check for reserve requirement
10909+ // Pre-check for reserve requirement
1088610910 // (Note: It should also be checked later at tx_complete)
10911+ let pre_channel_value = self.funding.get_value_satoshis();
10912+ let post_channel_value = self.funding.compute_post_splice_value(
10913+ our_funding_contribution_satoshis,
10914+ their_funding_contribution_satoshis,
10915+ );
10916+ let pre_balance_self = self.funding.value_to_self_msat;
10917+ let post_balance_self =
10918+ PendingSplice::add_checked(pre_balance_self, our_funding_contribution_satoshis);
10919+ let pre_balance_counterparty = pre_channel_value.saturating_sub(pre_balance_self);
10920+ let post_balance_counterparty = post_channel_value.saturating_sub(post_balance_self);
10921+ // Pre-check for reserve requirement
10922+ // This will also be checked later at tx_complete
10923+ let _res = self.check_splice_balances_meet_v2_reserve_requirements(
10924+ pre_balance_self,
10925+ post_balance_self,
10926+ pre_balance_counterparty,
10927+ post_balance_counterparty,
10928+ pre_channel_value,
10929+ post_channel_value,
10930+ )?;
1088710931
1088810932 log_info!(
1088910933 logger,
@@ -10910,6 +10954,11 @@ where
1091010954 let tx_msg_opt = interactive_tx_constructor.take_initiator_first_message();
1091110955
1091210956 debug_assert!(self.interactive_tx_signing_session.is_none());
10957+ let pending_splice = if let Some(ref mut pending_splice) = &mut self.pending_splice {
10958+ pending_splice
10959+ } else {
10960+ return Err(ChannelError::Ignore(format!("Channel is not in pending splice")));
10961+ };
1091310962 pending_splice.funding_negotiation = Some(FundingNegotiation::ConstructingTransaction(
1091410963 splice_funding,
1091510964 interactive_tx_constructor,
@@ -10970,6 +11019,85 @@ where
1097011019 ))
1097111020 }
1097211021
11022+ /// Check that post-splicing balance meets reserve requirements, but only if it met it pre-splice as well
11023+ /// Check that post-splicing balance meets reserve requirements, but only if it met it pre-splice as well.
11024+ /// Returns the minimum channel reserve (sats)
11025+ #[cfg(splicing)]
11026+ pub fn check_splice_balance_meets_v2_reserve_requirement_noerr(
11027+ &self, pre_balance: u64, post_balance: u64, pre_channel_value: u64,
11028+ post_channel_value: u64, dust_limit: u64,
11029+ ) -> Result<(), u64> {
11030+ let post_channel_reserve_sats =
11031+ get_v2_channel_reserve_satoshis(post_channel_value, dust_limit);
11032+ if post_balance >= post_channel_reserve_sats * 1000 {
11033+ return Ok(());
11034+ }
11035+ // We're not allowed to dip below the reserve once we've been above,
11036+ // check differently for originally v1 and v2 channels
11037+ if self.is_v2_established() {
11038+ let pre_channel_reserve_sats =
11039+ get_v2_channel_reserve_satoshis(pre_channel_value, dust_limit);
11040+ if pre_balance >= pre_channel_reserve_sats * 1000 {
11041+ return Err(post_channel_reserve_sats);
11042+ }
11043+ } else {
11044+ if pre_balance >= self.funding.holder_selected_channel_reserve_satoshis * 1000 {
11045+ return Err(post_channel_reserve_sats);
11046+ }
11047+ if let Some(cp_reserve) = self.funding.counterparty_selected_channel_reserve_satoshis {
11048+ if pre_balance >= cp_reserve * 1000 {
11049+ return Err(post_channel_reserve_sats);
11050+ }
11051+ }
11052+ }
11053+ // Make sure we either remain with the same balance or move towards the reserve.
11054+ if post_balance >= pre_balance {
11055+ Ok(())
11056+ } else {
11057+ Err(post_channel_reserve_sats)
11058+ }
11059+ }
11060+
11061+ /// Check that balances meet the channel reserve requirements or violates them (below reserve).
11062+ /// The channel value is an input as opposed to using from the FundingScope, so that this can be used in case of splicing
11063+ /// to check with new channel value (before being committed to it).
11064+ #[cfg(splicing)]
11065+ pub fn check_splice_balances_meet_v2_reserve_requirements(
11066+ &self, self_balance_pre_msat: u64, self_balance_post_msat: u64,
11067+ counterparty_balance_pre_msat: u64, counterparty_balance_post_msat: u64,
11068+ channel_value_pre: u64, channel_value_post: u64,
11069+ ) -> Result<(), ChannelError> {
11070+ let is_ok_self = self.check_splice_balance_meets_v2_reserve_requirement_noerr(
11071+ self_balance_pre_msat,
11072+ self_balance_post_msat,
11073+ channel_value_pre,
11074+ channel_value_post,
11075+ self.context.holder_dust_limit_satoshis,
11076+ );
11077+ if let Err(channel_reserve_self) = is_ok_self {
11078+ return Err(ChannelError::Warn(format!(
11079+ "Balance below reserve, mandated by holder, {} vs {}",
11080+ self_balance_post_msat, channel_reserve_self,
11081+ )));
11082+ }
11083+ let is_ok_cp = self.check_splice_balance_meets_v2_reserve_requirement_noerr(
11084+ counterparty_balance_pre_msat,
11085+ counterparty_balance_post_msat,
11086+ channel_value_pre,
11087+ channel_value_post,
11088+ self.context.counterparty_dust_limit_satoshis,
11089+ );
11090+ if let Err(channel_reserve_cp) = is_ok_cp {
11091+ return Err(ChannelError::Warn(format!(
11092+ "Balance below reserve mandated by counterparty, {} vs {}",
11093+ counterparty_balance_post_msat, channel_reserve_cp,
11094+ )));
11095+ }
11096+ Ok(())
11097+ }
11098+
11099+ // Send stuff to our remote peers:
11100+
1097311101 /// Queues up an outbound HTLC to send by placing it in the holding cell. You should call
1097411102 /// [`Self::maybe_free_holding_cell_htlcs`] in order to actually generate and send the
1097511103 /// commitment update.
0 commit comments