Skip to content

Commit 0f3914c

Browse files
committed
Add check for channel reserve
1 parent 00ee0ef commit 0f3914c

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
@@ -1728,6 +1728,23 @@ struct PendingSplice {
17281728
pub our_funding_contribution: i64,
17291729
}
17301730

1731+
#[cfg(splicing)]
1732+
impl PendingSplice {
1733+
#[inline]
1734+
fn add_checked(base: u64, delta: i64) -> u64 {
1735+
if delta >= 0 {
1736+
base.saturating_add(delta as u64)
1737+
} else {
1738+
base.saturating_sub(delta.abs() as u64)
1739+
}
1740+
}
1741+
1742+
/// Compute the post-splice channel value from the pre-splice values and the peer contributions
1743+
pub fn compute_post_value(pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64) -> u64 {
1744+
Self::add_checked(pre_channel_value, our_funding_contribution.saturating_add(their_funding_contribution))
1745+
}
1746+
}
1747+
17311748
/// Contains everything about the channel including state, and various flags.
17321749
pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
17331750
config: LegacyChannelConfig,
@@ -4249,6 +4266,59 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
42494266
}
42504267
}
42514268

4269+
/// Check that post-splicing balance meets reserve requirements, but only if it met it pre-splice as well
4270+
#[cfg(splicing)]
4271+
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> {
4272+
if post_balance == 0 {
4273+
// 0 balance is fine
4274+
return Ok(());
4275+
}
4276+
let post_channel_reserve = get_v2_channel_reserve_satoshis(post_channel_value, dust_limit);
4277+
if post_balance >= post_channel_reserve {
4278+
return Ok(());
4279+
}
4280+
// post is not OK, check pre
4281+
if pre_balance == 0 {
4282+
// pre OK, post not -> not
4283+
return Err(post_channel_reserve);
4284+
}
4285+
let pre_channel_reserve = get_v2_channel_reserve_satoshis(pre_channel_value, dust_limit);
4286+
if pre_balance >= pre_channel_reserve {
4287+
// pre OK, post not -> not
4288+
return Err(post_channel_reserve);
4289+
}
4290+
// post not OK, but so was pre -> OK
4291+
Ok(())
4292+
}
4293+
4294+
/// Check that balances meet the channel reserve requirements or violates them (below reserve).
4295+
/// The channel value is an input as opposed to using from the FundingScope, so that this can be used in case of splicing
4296+
/// to check with new channel value (before being committed to it).
4297+
#[cfg(splicing)]
4298+
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> {
4299+
let is_ok_self = Self::check_splice_balance_meets_v2_reserve_requirement_noerr(
4300+
self_balance_pre, self_balance_post, channel_value_pre, channel_value_post,
4301+
self.holder_dust_limit_satoshis
4302+
);
4303+
if let Err(channel_reserve_self) = is_ok_self {
4304+
return Err(ChannelError::Warn(format!(
4305+
"Balance below reserve, mandated by holder, {} vs {}",
4306+
self_balance_post, channel_reserve_self,
4307+
)));
4308+
}
4309+
let is_ok_cp = Self::check_splice_balance_meets_v2_reserve_requirement_noerr(
4310+
counterparty_balance_pre, counterparty_balance_post, channel_value_pre, channel_value_post,
4311+
self.counterparty_dust_limit_satoshis
4312+
);
4313+
if let Err(channel_reserve_cp) = is_ok_cp {
4314+
return Err(ChannelError::Warn(format!(
4315+
"Balance below reserve mandated by counterparty, {} vs {}",
4316+
counterparty_balance_post, channel_reserve_cp,
4317+
)));
4318+
}
4319+
Ok(())
4320+
}
4321+
42524322
/// Get the commitment tx fee for the local's (i.e. our) next commitment transaction based on the
42534323
/// number of pending HTLCs that are on track to be in our next commitment tx.
42544324
///
@@ -8542,14 +8612,28 @@ impl<SP: Deref> FundedChannel<SP> where
85428612

85438613
/// Handle splice_ack
85448614
#[cfg(splicing)]
8545-
pub fn splice_ack(&mut self, _msg: &msgs::SpliceAck) -> Result<(), ChannelError> {
8615+
pub fn splice_ack(&mut self, msg: &msgs::SpliceAck) -> Result<(), ChannelError> {
85468616
// check if splice is pending
8547-
if self.pending_splice.is_none() {
8617+
let pending_splice = if let Some(pending_splice) = &self.pending_splice {
8618+
pending_splice
8619+
} else {
85488620
return Err(ChannelError::Warn(format!("Channel is not in pending splice")));
85498621
};
85508622

8551-
// TODO(splicing): Pre-check for reserve requirement
8623+
// Pre-check for reserve requirement
85528624
// (Note: It should also be checked later at tx_complete)
8625+
let our_funding_contribution = pending_splice.our_funding_contribution;
8626+
let their_funding_contribution_satoshis = msg.funding_contribution_satoshis;
8627+
8628+
let pre_channel_value = self.funding.get_value_satoshis();
8629+
let post_channel_value = PendingSplice::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution_satoshis);
8630+
let pre_balance_self = self.funding.value_to_self_msat;
8631+
let post_balance_self = PendingSplice::add_checked(pre_balance_self, our_funding_contribution);
8632+
let pre_balance_counterparty = pre_channel_value.saturating_sub(pre_balance_self);
8633+
let post_balance_counterparty = post_channel_value.saturating_sub(post_balance_self);
8634+
// Pre-check for reserve requirement
8635+
// This will also be checked later at tx_complete
8636+
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)?;
85538637
Ok(())
85548638
}
85558639

@@ -13061,4 +13145,69 @@ mod tests {
1306113145
);
1306213146
}
1306313147
}
13148+
13149+
#[cfg(splicing)]
13150+
fn get_pre_and_post(pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64) -> (u64, u64) {
13151+
use crate::ln::channel::PendingSplice;
13152+
13153+
let post_channel_value = PendingSplice::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution);
13154+
(pre_channel_value, post_channel_value)
13155+
}
13156+
13157+
#[cfg(splicing)]
13158+
#[test]
13159+
fn test_splice_compute_post_value() {
13160+
{
13161+
// increase, small amounts
13162+
let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 6_000, 0);
13163+
assert_eq!(pre_channel_value, 9_000);
13164+
assert_eq!(post_channel_value, 15_000);
13165+
}
13166+
{
13167+
// increase, small amounts
13168+
let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 4_000, 2_000);
13169+
assert_eq!(pre_channel_value, 9_000);
13170+
assert_eq!(post_channel_value, 15_000);
13171+
}
13172+
{
13173+
// increase, small amounts
13174+
let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 0, 6_000);
13175+
assert_eq!(pre_channel_value, 9_000);
13176+
assert_eq!(post_channel_value, 15_000);
13177+
}
13178+
{
13179+
// decrease, small amounts
13180+
let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -6_000, 0);
13181+
assert_eq!(pre_channel_value, 15_000);
13182+
assert_eq!(post_channel_value, 9_000);
13183+
}
13184+
{
13185+
// decrease, small amounts
13186+
let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -4_000, -2_000);
13187+
assert_eq!(pre_channel_value, 15_000);
13188+
assert_eq!(post_channel_value, 9_000);
13189+
}
13190+
{
13191+
// increase and decrease
13192+
let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, 4_000, -2_000);
13193+
assert_eq!(pre_channel_value, 15_000);
13194+
assert_eq!(post_channel_value, 17_000);
13195+
}
13196+
let base2: u64 = 2;
13197+
let huge63i3 = (base2.pow(63) - 3) as i64;
13198+
assert_eq!(huge63i3, 9223372036854775805);
13199+
assert_eq!(-huge63i3, -9223372036854775805);
13200+
{
13201+
// increase, large amount
13202+
let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, 3);
13203+
assert_eq!(pre_channel_value, 9_000);
13204+
assert_eq!(post_channel_value, 9223372036854784807);
13205+
}
13206+
{
13207+
// increase, large amounts
13208+
let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, huge63i3);
13209+
assert_eq!(pre_channel_value, 9_000);
13210+
assert_eq!(post_channel_value, 9223372036854784807);
13211+
}
13212+
}
1306413213
}

0 commit comments

Comments
 (0)