@@ -2477,6 +2477,28 @@ impl<'a> From<&'a Transaction> for ConfirmedTransaction<'a> {
2477
2477
}
2478
2478
}
2479
2479
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
+
2480
2502
/// Contains everything about the channel including state, and various flags.
2481
2503
pub(super) struct ChannelContext<SP: Deref>
2482
2504
where
@@ -10839,35 +10861,37 @@ where
10839
10861
ES::Target: EntropySource,
10840
10862
L::Target: Logger,
10841
10863
{
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
+ };
10847
10870
10848
- // TODO(splicing): Add check that we are the splice (quiescence) initiator
10871
+ // TODO(splicing): Add check that we are the splice (quiescence) initiator
10849
10872
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
+ }
10871
10895
};
10872
10896
10873
10897
let our_funding_contribution_satoshis =
@@ -10882,8 +10906,28 @@ where
10882
10906
msg.funding_pubkey,
10883
10907
)?;
10884
10908
10885
- // TODO(splicing): Pre-check for reserve requirement
10909
+ // Pre-check for reserve requirement
10886
10910
// (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
+ )?;
10887
10931
10888
10932
log_info!(
10889
10933
logger,
@@ -10910,6 +10954,11 @@ where
10910
10954
let tx_msg_opt = interactive_tx_constructor.take_initiator_first_message();
10911
10955
10912
10956
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
+ };
10913
10962
pending_splice.funding_negotiation = Some(FundingNegotiation::ConstructingTransaction(
10914
10963
splice_funding,
10915
10964
interactive_tx_constructor,
@@ -10970,6 +11019,85 @@ where
10970
11019
))
10971
11020
}
10972
11021
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
+
10973
11101
/// Queues up an outbound HTLC to send by placing it in the holding cell. You should call
10974
11102
/// [`Self::maybe_free_holding_cell_htlcs`] in order to actually generate and send the
10975
11103
/// commitment update.
0 commit comments