Skip to content

Commit f94c434

Browse files
committed
New splice_channel() for initiating splicing, handle splice_init and splice_ack messages, but fail afterwards
1 parent 4c43a5b commit f94c434

File tree

1 file changed

+139
-11
lines changed

1 file changed

+139
-11
lines changed

lightning/src/ln/channel.rs

Lines changed: 139 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1519,7 +1519,7 @@ impl<SP: Deref> Channel<SP> where
15191519
holder_commitment_point,
15201520
is_v2_established: true,
15211521
#[cfg(splicing)]
1522-
pending_splice: None,
1522+
pending_splice_pre: None,
15231523
};
15241524
let res = funded_channel.commitment_signed_initial_v2(msg, best_block, signer_provider, logger)
15251525
.map(|monitor| (Some(monitor), None))
@@ -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,
@@ -4256,6 +4273,33 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
42564273
}
42574274
}
42584275

4276+
/// Check that a balance value meets the channel reserve requirements or violates them (below reserve).
4277+
/// The channel value is an input as opposed to using from self, so that this can be used in case of splicing
4278+
/// to checks with new channel value (before being comitted to it).
4279+
#[cfg(splicing)]
4280+
pub fn check_balance_meets_reserve_requirements(&self, balance: u64, channel_value: u64) -> Result<(), ChannelError> {
4281+
if balance == 0 {
4282+
return Ok(());
4283+
}
4284+
let holder_selected_channel_reserve_satoshis = get_v2_channel_reserve_satoshis(
4285+
channel_value, self.holder_dust_limit_satoshis);
4286+
if balance < holder_selected_channel_reserve_satoshis {
4287+
return Err(ChannelError::Warn(format!(
4288+
"Balance below reserve mandated by holder, {} vs {}",
4289+
balance, holder_selected_channel_reserve_satoshis,
4290+
)));
4291+
}
4292+
let counterparty_selected_channel_reserve_satoshis = get_v2_channel_reserve_satoshis(
4293+
channel_value, self.counterparty_dust_limit_satoshis);
4294+
if balance < counterparty_selected_channel_reserve_satoshis {
4295+
return Err(ChannelError::Warn(format!(
4296+
"Balance below reserve mandated by counterparty, {} vs {}",
4297+
balance, counterparty_selected_channel_reserve_satoshis,
4298+
)));
4299+
}
4300+
Ok(())
4301+
}
4302+
42594303
/// Get the commitment tx fee for the local's (i.e. our) next commitment transaction based on the
42604304
/// number of pending HTLCs that are on track to be in our next commitment tx.
42614305
///
@@ -4902,7 +4946,7 @@ pub(super) struct FundedChannel<SP: Deref> where SP::Target: SignerProvider {
49024946
is_v2_established: bool,
49034947
/// Info about an in-progress, pending splice (if any), on the pre-splice channel
49044948
#[cfg(splicing)]
4905-
pending_splice: Option<PendingSplice>,
4949+
pending_splice_pre: Option<PendingSplice>,
49064950
}
49074951

49084952
#[cfg(any(test, fuzzing))]
@@ -8435,7 +8479,7 @@ impl<SP: Deref> FundedChannel<SP> where
84358479
) -> Result<msgs::SpliceInit, APIError> {
84368480
// Check if a splice has been initiated already.
84378481
// Note: only a single outstanding splice is supported (per spec)
8438-
if let Some(splice_info) = &self.pending_splice {
8482+
if let Some(splice_info) = &self.pending_splice_pre {
84398483
return Err(APIError::APIMisuseError { err: format!(
84408484
"Channel {} cannot be spliced, as it has already a splice pending (contribution {})",
84418485
self.context.channel_id(), splice_info.our_funding_contribution
@@ -8471,7 +8515,7 @@ impl<SP: Deref> FundedChannel<SP> where
84718515
self.context.channel_id(), err,
84728516
)})?;
84738517

8474-
self.pending_splice = Some(PendingSplice {
8518+
self.pending_splice_pre = Some(PendingSplice {
84758519
our_funding_contribution: our_funding_contribution_satoshis,
84768520
});
84778521

@@ -8505,7 +8549,7 @@ impl<SP: Deref> FundedChannel<SP> where
85058549
let our_funding_contribution_satoshis = 0i64;
85068550

85078551
// Check if a splice has been initiated already.
8508-
if let Some(splice_info) = &self.pending_splice {
8552+
if let Some(splice_info) = &self.pending_splice_pre {
85098553
return Err(ChannelError::Warn(format!(
85108554
"Channel has already a splice pending, contribution {}", splice_info.our_funding_contribution,
85118555
)));
@@ -8531,7 +8575,13 @@ impl<SP: Deref> FundedChannel<SP> where
85318575

85328576
// Note on channel reserve requirement pre-check: as the splice acceptor does not contribute,
85338577
// it can't go below reserve, therefore no pre-check is done here.
8534-
// TODO(splicing): Once splice acceptor can contribute, add reserve pre-check, similar to the one in `splice_ack`.
8578+
8579+
let pre_channel_value = self.funding.value_to_self_msat;
8580+
let post_channel_value = PendingSplice::compute_post_value(pre_channel_value, their_funding_contribution_satoshis, our_funding_contribution_satoshis);
8581+
let post_balance = PendingSplice::add_checked(self.funding.value_to_self_msat, our_funding_contribution_satoshis);
8582+
// Early check for reserve requirement, assuming maximum balance of full channel value
8583+
// This will also be checked later at tx_complete
8584+
let _res = self.context.check_balance_meets_reserve_requirements(post_balance, post_channel_value)?;
85358585

85368586
// TODO(splicing): Store msg.funding_pubkey
85378587
// TODO(splicing): Apply start of splice (splice_start)
@@ -8550,14 +8600,27 @@ impl<SP: Deref> FundedChannel<SP> where
85508600

85518601
/// Handle splice_ack
85528602
#[cfg(splicing)]
8553-
pub fn splice_ack(&mut self, _msg: &msgs::SpliceAck) -> Result<(), ChannelError> {
8603+
pub fn splice_ack(&mut self, msg: &msgs::SpliceAck) -> Result<(), ChannelError> {
85548604
// check if splice is pending
8555-
if self.pending_splice.is_none() {
8605+
let pending_splice = if let Some(pending_splice) = &self.pending_splice_pre {
8606+
pending_splice
8607+
} else {
85568608
return Err(ChannelError::Warn(format!("Channel is not in pending splice")));
85578609
};
85588610

85598611
// TODO(splicing): Pre-check for reserve requirement
85608612
// (Note: It should also be checked later at tx_complete)
8613+
8614+
8615+
let our_funding_contribution = pending_splice.our_funding_contribution;
8616+
let their_funding_contribution_satoshis = msg.funding_contribution_satoshis;
8617+
8618+
let pre_channel_value = self.funding.get_value_satoshis();
8619+
let post_channel_value = PendingSplice::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution_satoshis);
8620+
let post_balance = PendingSplice::add_checked(self.funding.value_to_self_msat, our_funding_contribution);
8621+
// Early check for reserve requirement, assuming maximum balance of full channel value
8622+
// This will also be checked later at tx_complete
8623+
let _res = self.context.check_balance_meets_reserve_requirements(post_balance, post_channel_value)?;
85618624
Ok(())
85628625
}
85638626

@@ -9481,7 +9544,7 @@ impl<SP: Deref> OutboundV1Channel<SP> where SP::Target: SignerProvider {
94819544
is_v2_established: false,
94829545
holder_commitment_point,
94839546
#[cfg(splicing)]
9484-
pending_splice: None,
9547+
pending_splice_pre: None,
94859548
};
94869549

94879550
let need_channel_ready = channel.check_get_channel_ready(0, logger).is_some()
@@ -9758,7 +9821,7 @@ impl<SP: Deref> InboundV1Channel<SP> where SP::Target: SignerProvider {
97589821
is_v2_established: false,
97599822
holder_commitment_point,
97609823
#[cfg(splicing)]
9761-
pending_splice: None,
9824+
pending_splice_pre: None,
97629825
};
97639826
let need_channel_ready = channel.check_get_channel_ready(0, logger).is_some()
97649827
|| channel.context.signer_pending_channel_ready;
@@ -11120,7 +11183,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, &'c Channel
1112011183
is_v2_established,
1112111184
holder_commitment_point,
1112211185
#[cfg(splicing)]
11123-
pending_splice: None,
11186+
pending_splice_pre: None,
1112411187
})
1112511188
}
1112611189
}
@@ -13050,4 +13113,69 @@ mod tests {
1305013113
);
1305113114
}
1305213115
}
13116+
13117+
#[cfg(all(test, splicing))]
13118+
fn get_pre_and_post(pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64) -> (u64, u64) {
13119+
use crate::ln::channel::PendingSplice;
13120+
13121+
let post_channel_value = PendingSplice::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution);
13122+
(pre_channel_value, post_channel_value)
13123+
}
13124+
13125+
#[cfg(all(test, splicing))]
13126+
#[test]
13127+
fn test_splice_compute_post_value() {
13128+
{
13129+
// increase, small amounts
13130+
let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 6_000, 0);
13131+
assert_eq!(pre_channel_value, 9_000);
13132+
assert_eq!(post_channel_value, 15_000);
13133+
}
13134+
{
13135+
// increase, small amounts
13136+
let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 4_000, 2_000);
13137+
assert_eq!(pre_channel_value, 9_000);
13138+
assert_eq!(post_channel_value, 15_000);
13139+
}
13140+
{
13141+
// increase, small amounts
13142+
let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 0, 6_000);
13143+
assert_eq!(pre_channel_value, 9_000);
13144+
assert_eq!(post_channel_value, 15_000);
13145+
}
13146+
{
13147+
// decrease, small amounts
13148+
let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -6_000, 0);
13149+
assert_eq!(pre_channel_value, 15_000);
13150+
assert_eq!(post_channel_value, 9_000);
13151+
}
13152+
{
13153+
// decrease, small amounts
13154+
let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -4_000, -2_000);
13155+
assert_eq!(pre_channel_value, 15_000);
13156+
assert_eq!(post_channel_value, 9_000);
13157+
}
13158+
{
13159+
// increase and decrease
13160+
let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, 4_000, -2_000);
13161+
assert_eq!(pre_channel_value, 15_000);
13162+
assert_eq!(post_channel_value, 17_000);
13163+
}
13164+
let base2: u64 = 2;
13165+
let huge63i3 = (base2.pow(63) - 3) as i64;
13166+
assert_eq!(huge63i3, 9223372036854775805);
13167+
assert_eq!(-huge63i3, -9223372036854775805);
13168+
{
13169+
// increase, large amount
13170+
let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, 3);
13171+
assert_eq!(pre_channel_value, 9_000);
13172+
assert_eq!(post_channel_value, 9223372036854784807);
13173+
}
13174+
{
13175+
// increase, large amounts
13176+
let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, huge63i3);
13177+
assert_eq!(pre_channel_value, 9_000);
13178+
assert_eq!(post_channel_value, 9223372036854784807);
13179+
}
13180+
}
1305313181
}

0 commit comments

Comments
 (0)