Skip to content

Commit 8e77e2a

Browse files
committed
Add check for channel reserve
1 parent 0fe51c5 commit 8e77e2a

File tree

1 file changed

+152
-3
lines changed

1 file changed

+152
-3
lines changed

lightning/src/ln/channel.rs

Lines changed: 152 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2242,6 +2242,23 @@ impl<'a> From<&'a Transaction> for ConfirmedTransaction<'a> {
22422242
}
22432243
}
22442244

2245+
#[cfg(splicing)]
2246+
impl PendingSplice {
2247+
#[inline]
2248+
fn add_checked(base: u64, delta: i64) -> u64 {
2249+
if delta >= 0 {
2250+
base.saturating_add(delta as u64)
2251+
} else {
2252+
base.saturating_sub(delta.abs() as u64)
2253+
}
2254+
}
2255+
2256+
/// Compute the post-splice channel value from the pre-splice values and the peer contributions
2257+
pub fn compute_post_value(pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64) -> u64 {
2258+
Self::add_checked(pre_channel_value, our_funding_contribution.saturating_add(their_funding_contribution))
2259+
}
2260+
}
2261+
22452262
/// Contains everything about the channel including state, and various flags.
22462263
pub(super) struct ChannelContext<SP: Deref>
22472264
where
@@ -5098,6 +5115,59 @@ where
50985115
}
50995116
}
51005117

5118+
/// Check that post-splicing balance meets reserve requirements, but only if it met it pre-splice as well
5119+
#[cfg(splicing)]
5120+
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> {
5121+
if post_balance == 0 {
5122+
// 0 balance is fine
5123+
return Ok(());
5124+
}
5125+
let post_channel_reserve = get_v2_channel_reserve_satoshis(post_channel_value, dust_limit);
5126+
if post_balance >= post_channel_reserve {
5127+
return Ok(());
5128+
}
5129+
// post is not OK, check pre
5130+
if pre_balance == 0 {
5131+
// pre OK, post not -> not
5132+
return Err(post_channel_reserve);
5133+
}
5134+
let pre_channel_reserve = get_v2_channel_reserve_satoshis(pre_channel_value, dust_limit);
5135+
if pre_balance >= pre_channel_reserve {
5136+
// pre OK, post not -> not
5137+
return Err(post_channel_reserve);
5138+
}
5139+
// post not OK, but so was pre -> OK
5140+
Ok(())
5141+
}
5142+
5143+
/// Check that balances meet the channel reserve requirements or violates them (below reserve).
5144+
/// The channel value is an input as opposed to using from the FundingScope, so that this can be used in case of splicing
5145+
/// to check with new channel value (before being committed to it).
5146+
#[cfg(splicing)]
5147+
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> {
5148+
let is_ok_self = Self::check_splice_balance_meets_v2_reserve_requirement_noerr(
5149+
self_balance_pre, self_balance_post, channel_value_pre, channel_value_post,
5150+
self.holder_dust_limit_satoshis
5151+
);
5152+
if let Err(channel_reserve_self) = is_ok_self {
5153+
return Err(ChannelError::Warn(format!(
5154+
"Balance below reserve, mandated by holder, {} vs {}",
5155+
self_balance_post, channel_reserve_self,
5156+
)));
5157+
}
5158+
let is_ok_cp = Self::check_splice_balance_meets_v2_reserve_requirement_noerr(
5159+
counterparty_balance_pre, counterparty_balance_post, channel_value_pre, channel_value_post,
5160+
self.counterparty_dust_limit_satoshis
5161+
);
5162+
if let Err(channel_reserve_cp) = is_ok_cp {
5163+
return Err(ChannelError::Warn(format!(
5164+
"Balance below reserve mandated by counterparty, {} vs {}",
5165+
counterparty_balance_post, channel_reserve_cp,
5166+
)));
5167+
}
5168+
Ok(())
5169+
}
5170+
51015171
/// Get the commitment tx fee for the local's (i.e. our) next commitment transaction based on the
51025172
/// number of pending HTLCs that are on track to be in our next commitment tx.
51035173
///
@@ -10334,14 +10404,28 @@ where
1033410404

1033510405
/// Handle splice_ack
1033610406
#[cfg(splicing)]
10337-
pub fn splice_ack(&mut self, _msg: &msgs::SpliceAck) -> Result<(), ChannelError> {
10407+
pub fn splice_ack(&mut self, msg: &msgs::SpliceAck) -> Result<(), ChannelError> {
1033810408
// check if splice is pending
10339-
if self.pending_splice.is_none() {
10409+
let pending_splice = if let Some(pending_splice) = &self.pending_splice {
10410+
pending_splice
10411+
} else {
1034010412
return Err(ChannelError::Warn(format!("Channel is not in pending splice")));
1034110413
};
1034210414

10343-
// TODO(splicing): Pre-check for reserve requirement
10415+
// Pre-check for reserve requirement
1034410416
// (Note: It should also be checked later at tx_complete)
10417+
let our_funding_contribution = pending_splice.our_funding_contribution;
10418+
let their_funding_contribution_satoshis = msg.funding_contribution_satoshis;
10419+
10420+
let pre_channel_value = self.funding.get_value_satoshis();
10421+
let post_channel_value = PendingSplice::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution_satoshis);
10422+
let pre_balance_self = self.funding.value_to_self_msat;
10423+
let post_balance_self = PendingSplice::add_checked(pre_balance_self, our_funding_contribution);
10424+
let pre_balance_counterparty = pre_channel_value.saturating_sub(pre_balance_self);
10425+
let post_balance_counterparty = post_channel_value.saturating_sub(post_balance_self);
10426+
// Pre-check for reserve requirement
10427+
// This will also be checked later at tx_complete
10428+
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)?;
1034510429
Ok(())
1034610430
}
1034710431

@@ -15106,4 +15190,69 @@ mod tests {
1510615190
);
1510715191
}
1510815192
}
15193+
15194+
#[cfg(splicing)]
15195+
fn get_pre_and_post(pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64) -> (u64, u64) {
15196+
use crate::ln::channel::PendingSplice;
15197+
15198+
let post_channel_value = PendingSplice::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution);
15199+
(pre_channel_value, post_channel_value)
15200+
}
15201+
15202+
#[cfg(splicing)]
15203+
#[test]
15204+
fn test_splice_compute_post_value() {
15205+
{
15206+
// increase, small amounts
15207+
let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 6_000, 0);
15208+
assert_eq!(pre_channel_value, 9_000);
15209+
assert_eq!(post_channel_value, 15_000);
15210+
}
15211+
{
15212+
// increase, small amounts
15213+
let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 4_000, 2_000);
15214+
assert_eq!(pre_channel_value, 9_000);
15215+
assert_eq!(post_channel_value, 15_000);
15216+
}
15217+
{
15218+
// increase, small amounts
15219+
let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 0, 6_000);
15220+
assert_eq!(pre_channel_value, 9_000);
15221+
assert_eq!(post_channel_value, 15_000);
15222+
}
15223+
{
15224+
// decrease, small amounts
15225+
let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -6_000, 0);
15226+
assert_eq!(pre_channel_value, 15_000);
15227+
assert_eq!(post_channel_value, 9_000);
15228+
}
15229+
{
15230+
// decrease, small amounts
15231+
let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -4_000, -2_000);
15232+
assert_eq!(pre_channel_value, 15_000);
15233+
assert_eq!(post_channel_value, 9_000);
15234+
}
15235+
{
15236+
// increase and decrease
15237+
let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, 4_000, -2_000);
15238+
assert_eq!(pre_channel_value, 15_000);
15239+
assert_eq!(post_channel_value, 17_000);
15240+
}
15241+
let base2: u64 = 2;
15242+
let huge63i3 = (base2.pow(63) - 3) as i64;
15243+
assert_eq!(huge63i3, 9223372036854775805);
15244+
assert_eq!(-huge63i3, -9223372036854775805);
15245+
{
15246+
// increase, large amount
15247+
let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, 3);
15248+
assert_eq!(pre_channel_value, 9_000);
15249+
assert_eq!(post_channel_value, 9223372036854784807);
15250+
}
15251+
{
15252+
// increase, large amounts
15253+
let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, huge63i3);
15254+
assert_eq!(pre_channel_value, 9_000);
15255+
assert_eq!(post_channel_value, 9223372036854784807);
15256+
}
15257+
}
1510915258
}

0 commit comments

Comments
 (0)