Skip to content

Commit ba195ce

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

File tree

1 file changed

+155
-28
lines changed

1 file changed

+155
-28
lines changed

lightning/src/ln/channel.rs

Lines changed: 155 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,84 @@ where
1097011019
))
1097111020
}
1097211021

11022+
/// Check that post-splicing balance meets reserve requirements, but only if it met it pre-splice as well.
11023+
/// In case of error, it returns the minimum channel reserve that was violated (in sats)
11024+
#[cfg(splicing)]
11025+
pub fn check_splice_balance_meets_v2_reserve_requirement(
11026+
&self, pre_balance_msat: u64, post_balance_msat: u64, pre_channel_value_sats: u64,
11027+
post_channel_value_sats: u64, dust_limit_sats: u64,
11028+
) -> Result<(), u64> {
11029+
let post_channel_reserve_sats =
11030+
get_v2_channel_reserve_satoshis(post_channel_value_sats, dust_limit_sats);
11031+
if post_balance_msat >= (post_channel_reserve_sats * 1000) {
11032+
return Ok(());
11033+
}
11034+
// We're not allowed to dip below the reserve once we've been above,
11035+
// check differently for originally v1 and v2 channels
11036+
if self.is_v2_established() {
11037+
let pre_channel_reserve_sats =
11038+
get_v2_channel_reserve_satoshis(pre_channel_value_sats, dust_limit_sats);
11039+
if pre_balance_msat >= (pre_channel_reserve_sats * 1000) {
11040+
return Err(post_channel_reserve_sats);
11041+
}
11042+
} else {
11043+
if pre_balance_msat >= (self.funding.holder_selected_channel_reserve_satoshis * 1000) {
11044+
return Err(post_channel_reserve_sats);
11045+
}
11046+
if let Some(cp_reserve) = self.funding.counterparty_selected_channel_reserve_satoshis {
11047+
if pre_balance_msat >= (cp_reserve * 1000) {
11048+
return Err(post_channel_reserve_sats);
11049+
}
11050+
}
11051+
}
11052+
// Make sure we either remain with the same balance or move towards the reserve.
11053+
if post_balance_msat >= pre_balance_msat {
11054+
Ok(())
11055+
} else {
11056+
Err(post_channel_reserve_sats)
11057+
}
11058+
}
11059+
11060+
/// Check that balances (self and counterparty) meet the channel reserve requirements or violates them (below reserve).
11061+
/// The channel value is an input as opposed to using from the FundingScope, so that this can be used in case of splicing
11062+
/// to check with new channel value (before being committed to it).
11063+
#[cfg(splicing)]
11064+
pub fn check_splice_balances_meet_v2_reserve_requirements(
11065+
&self, self_balance_pre_msat: u64, self_balance_post_msat: u64,
11066+
counterparty_balance_pre_msat: u64, counterparty_balance_post_msat: u64,
11067+
channel_value_pre_sats: u64, channel_value_post_sats: u64,
11068+
) -> Result<(), ChannelError> {
11069+
let is_ok_self = self.check_splice_balance_meets_v2_reserve_requirement(
11070+
self_balance_pre_msat,
11071+
self_balance_post_msat,
11072+
channel_value_pre_sats,
11073+
channel_value_post_sats,
11074+
self.context.holder_dust_limit_satoshis,
11075+
);
11076+
if let Err(channel_reserve_self) = is_ok_self {
11077+
return Err(ChannelError::Warn(format!(
11078+
"Balance below reserve, mandated by holder, {} vs {}",
11079+
self_balance_post_msat, channel_reserve_self,
11080+
)));
11081+
}
11082+
let is_ok_cp = self.check_splice_balance_meets_v2_reserve_requirement(
11083+
counterparty_balance_pre_msat,
11084+
counterparty_balance_post_msat,
11085+
channel_value_pre_sats,
11086+
channel_value_post_sats,
11087+
self.context.counterparty_dust_limit_satoshis,
11088+
);
11089+
if let Err(channel_reserve_cp) = is_ok_cp {
11090+
return Err(ChannelError::Warn(format!(
11091+
"Balance below reserve mandated by counterparty, {} vs {}",
11092+
counterparty_balance_post_msat, channel_reserve_cp,
11093+
)));
11094+
}
11095+
Ok(())
11096+
}
11097+
11098+
// Send stuff to our remote peers:
11099+
1097311100
/// Queues up an outbound HTLC to send by placing it in the holding cell. You should call
1097411101
/// [`Self::maybe_free_holding_cell_htlcs`] in order to actually generate and send the
1097511102
/// commitment update.

0 commit comments

Comments
 (0)