Skip to content

Commit 52cecb4

Browse files
committed
Add check for channel reserve
1 parent d3dd617 commit 52cecb4

File tree

1 file changed

+176
-3
lines changed

1 file changed

+176
-3
lines changed

lightning/src/ln/channel.rs

Lines changed: 176 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2238,6 +2238,28 @@ impl<'a> From<&'a Transaction> for ConfirmedTransaction<'a> {
22382238
}
22392239
}
22402240

2241+
#[cfg(splicing)]
2242+
impl PendingSplice {
2243+
#[inline]
2244+
fn add_checked(base: u64, delta: i64) -> u64 {
2245+
if delta >= 0 {
2246+
base.saturating_add(delta as u64)
2247+
} else {
2248+
base.saturating_sub(delta.abs() as u64)
2249+
}
2250+
}
2251+
2252+
/// Compute the post-splice channel value from the pre-splice values and the peer contributions
2253+
pub fn compute_post_value(
2254+
pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64,
2255+
) -> u64 {
2256+
Self::add_checked(
2257+
pre_channel_value,
2258+
our_funding_contribution.saturating_add(their_funding_contribution),
2259+
)
2260+
}
2261+
}
2262+
22412263
/// Contains everything about the channel including state, and various flags.
22422264
pub(super) struct ChannelContext<SP: Deref>
22432265
where
@@ -5051,6 +5073,71 @@ where
50515073
}
50525074
}
50535075

5076+
/// Check that post-splicing balance meets reserve requirements, but only if it met it pre-splice as well
5077+
#[cfg(splicing)]
5078+
pub fn check_splice_balance_meets_v2_reserve_requirement_noerr(
5079+
pre_balance: u64, post_balance: u64, pre_channel_value: u64, post_channel_value: u64,
5080+
dust_limit: u64,
5081+
) -> Result<(), u64> {
5082+
if post_balance == 0 {
5083+
// 0 balance is fine
5084+
return Ok(());
5085+
}
5086+
let post_channel_reserve = get_v2_channel_reserve_satoshis(post_channel_value, dust_limit);
5087+
if post_balance >= post_channel_reserve {
5088+
return Ok(());
5089+
}
5090+
// post is not OK, check pre
5091+
if pre_balance == 0 {
5092+
// pre OK, post not -> not
5093+
return Err(post_channel_reserve);
5094+
}
5095+
let pre_channel_reserve = get_v2_channel_reserve_satoshis(pre_channel_value, dust_limit);
5096+
if pre_balance >= pre_channel_reserve {
5097+
// pre OK, post not -> not
5098+
return Err(post_channel_reserve);
5099+
}
5100+
// post not OK, but so was pre -> OK
5101+
Ok(())
5102+
}
5103+
5104+
/// Check that balances meet the channel reserve requirements or violates them (below reserve).
5105+
/// The channel value is an input as opposed to using from the FundingScope, so that this can be used in case of splicing
5106+
/// to check with new channel value (before being committed to it).
5107+
#[cfg(splicing)]
5108+
pub fn check_splice_balances_meet_v2_reserve_requirements(
5109+
&self, self_balance_pre: u64, self_balance_post: u64, counterparty_balance_pre: u64,
5110+
counterparty_balance_post: u64, channel_value_pre: u64, channel_value_post: u64,
5111+
) -> Result<(), ChannelError> {
5112+
let is_ok_self = Self::check_splice_balance_meets_v2_reserve_requirement_noerr(
5113+
self_balance_pre,
5114+
self_balance_post,
5115+
channel_value_pre,
5116+
channel_value_post,
5117+
self.holder_dust_limit_satoshis,
5118+
);
5119+
if let Err(channel_reserve_self) = is_ok_self {
5120+
return Err(ChannelError::Warn(format!(
5121+
"Balance below reserve, mandated by holder, {} vs {}",
5122+
self_balance_post, channel_reserve_self,
5123+
)));
5124+
}
5125+
let is_ok_cp = Self::check_splice_balance_meets_v2_reserve_requirement_noerr(
5126+
counterparty_balance_pre,
5127+
counterparty_balance_post,
5128+
channel_value_pre,
5129+
channel_value_post,
5130+
self.counterparty_dust_limit_satoshis,
5131+
);
5132+
if let Err(channel_reserve_cp) = is_ok_cp {
5133+
return Err(ChannelError::Warn(format!(
5134+
"Balance below reserve mandated by counterparty, {} vs {}",
5135+
counterparty_balance_post, channel_reserve_cp,
5136+
)));
5137+
}
5138+
Ok(())
5139+
}
5140+
50545141
/// Get the commitment tx fee for the local's (i.e. our) next commitment transaction based on the
50555142
/// number of pending HTLCs that are on track to be in our next commitment tx.
50565143
///
@@ -10517,14 +10604,28 @@ where
1051710604

1051810605
/// Handle splice_ack
1051910606
#[cfg(splicing)]
10520-
pub fn splice_ack(&mut self, _msg: &msgs::SpliceAck) -> Result<(), ChannelError> {
10607+
pub fn splice_ack(&mut self, msg: &msgs::SpliceAck) -> Result<(), ChannelError> {
1052110608
// check if splice is pending
10522-
if self.pending_splice.is_none() {
10609+
let pending_splice = if let Some(pending_splice) = &self.pending_splice {
10610+
pending_splice
10611+
} else {
1052310612
return Err(ChannelError::Warn(format!("Channel is not in pending splice")));
1052410613
};
1052510614

10526-
// TODO(splicing): Pre-check for reserve requirement
10615+
// Pre-check for reserve requirement
1052710616
// (Note: It should also be checked later at tx_complete)
10617+
let our_funding_contribution = pending_splice.our_funding_contribution;
10618+
let their_funding_contribution_satoshis = msg.funding_contribution_satoshis;
10619+
10620+
let pre_channel_value = self.funding.get_value_satoshis();
10621+
let post_channel_value = PendingSplice::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution_satoshis);
10622+
let pre_balance_self = self.funding.value_to_self_msat;
10623+
let post_balance_self = PendingSplice::add_checked(pre_balance_self, our_funding_contribution);
10624+
let pre_balance_counterparty = pre_channel_value.saturating_sub(pre_balance_self);
10625+
let post_balance_counterparty = post_channel_value.saturating_sub(post_balance_self);
10626+
// Pre-check for reserve requirement
10627+
// This will also be checked later at tx_complete
10628+
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)?;
1052810629
Ok(())
1052910630
}
1053010631

@@ -15559,4 +15660,76 @@ mod tests {
1555915660
);
1556015661
}
1556115662
}
15663+
15664+
#[cfg(splicing)]
15665+
fn get_pre_and_post(
15666+
pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64,
15667+
) -> (u64, u64) {
15668+
use crate::ln::channel::PendingSplice;
15669+
15670+
let post_channel_value = PendingSplice::compute_post_value(
15671+
pre_channel_value,
15672+
our_funding_contribution,
15673+
their_funding_contribution,
15674+
);
15675+
(pre_channel_value, post_channel_value)
15676+
}
15677+
15678+
#[cfg(splicing)]
15679+
#[test]
15680+
fn test_splice_compute_post_value() {
15681+
{
15682+
// increase, small amounts
15683+
let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 6_000, 0);
15684+
assert_eq!(pre_channel_value, 9_000);
15685+
assert_eq!(post_channel_value, 15_000);
15686+
}
15687+
{
15688+
// increase, small amounts
15689+
let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 4_000, 2_000);
15690+
assert_eq!(pre_channel_value, 9_000);
15691+
assert_eq!(post_channel_value, 15_000);
15692+
}
15693+
{
15694+
// increase, small amounts
15695+
let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 0, 6_000);
15696+
assert_eq!(pre_channel_value, 9_000);
15697+
assert_eq!(post_channel_value, 15_000);
15698+
}
15699+
{
15700+
// decrease, small amounts
15701+
let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -6_000, 0);
15702+
assert_eq!(pre_channel_value, 15_000);
15703+
assert_eq!(post_channel_value, 9_000);
15704+
}
15705+
{
15706+
// decrease, small amounts
15707+
let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -4_000, -2_000);
15708+
assert_eq!(pre_channel_value, 15_000);
15709+
assert_eq!(post_channel_value, 9_000);
15710+
}
15711+
{
15712+
// increase and decrease
15713+
let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, 4_000, -2_000);
15714+
assert_eq!(pre_channel_value, 15_000);
15715+
assert_eq!(post_channel_value, 17_000);
15716+
}
15717+
let base2: u64 = 2;
15718+
let huge63i3 = (base2.pow(63) - 3) as i64;
15719+
assert_eq!(huge63i3, 9223372036854775805);
15720+
assert_eq!(-huge63i3, -9223372036854775805);
15721+
{
15722+
// increase, large amount
15723+
let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, 3);
15724+
assert_eq!(pre_channel_value, 9_000);
15725+
assert_eq!(post_channel_value, 9223372036854784807);
15726+
}
15727+
{
15728+
// increase, large amounts
15729+
let (pre_channel_value, post_channel_value) =
15730+
get_pre_and_post(9_000, huge63i3, huge63i3);
15731+
assert_eq!(pre_channel_value, 9_000);
15732+
assert_eq!(post_channel_value, 9223372036854784807);
15733+
}
15734+
}
1556215735
}

0 commit comments

Comments
 (0)