Skip to content

Commit aeaf392

Browse files
committed
Add check for channel reserve
1 parent 1e1d300 commit aeaf392

File tree

1 file changed

+156
-28
lines changed

1 file changed

+156
-28
lines changed

lightning/src/ln/channel.rs

Lines changed: 156 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -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.
24812503
pub(super) struct ChannelContext<SP: Deref>
24822504
where
@@ -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

Comments
 (0)