Skip to content

Commit 3bfcb23

Browse files
committed
Move checks&handling to Channel, cleanup, (review)
1 parent 6ee1aee commit 3bfcb23

File tree

2 files changed

+131
-102
lines changed

2 files changed

+131
-102
lines changed

lightning/src/ln/channel.rs

+117-22
Original file line numberDiff line numberDiff line change
@@ -1181,12 +1181,13 @@ impl UnfundedChannelContext {
11811181
/// Info about a pending splice, used in the pre-splice channel
11821182
#[cfg(splicing)]
11831183
#[derive(Clone)]
1184-
pub(crate) struct PendingSpliceInfoPre {
1184+
struct PendingSpliceInfoPre {
11851185
pub our_funding_contribution: i64,
11861186
}
11871187

11881188
#[cfg(splicing)]
11891189
impl PendingSpliceInfoPre {
1190+
#[inline]
11901191
fn add_checked(base: u64, delta: i64) -> u64 {
11911192
if delta >= 0 {
11921193
base.saturating_add(delta as u64)
@@ -1238,7 +1239,7 @@ pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
12381239

12391240
/// Info about an in-progress, pending splice (if any), on the pre-splice channel
12401241
#[cfg(splicing)]
1241-
pub(crate) pending_splice_pre: Option<PendingSpliceInfoPre>,
1242+
pending_splice_pre: Option<PendingSpliceInfoPre>,
12421243

12431244
latest_monitor_update_id: u64,
12441245

@@ -3419,7 +3420,8 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
34193420
}
34203421

34213422
/// Check that a balance value meets the channel reserve requirements or violates them (below reserve).
3422-
/// The channel value is an input, so that this can be used for checks with new planned channel value.
3423+
/// The channel value is an input as opposed to using from self, so that this can be used in case of splicing
3424+
/// to checks with new channel value (before being comitted to it).
34233425
#[cfg(any(dual_funding, splicing))]
34243426
pub fn check_balance_meets_reserve_requirements(&self, channel_value: u64, balance: u64) -> Result<(), ChannelError> {
34253427
let holder_selected_channel_reserve_satoshis = get_v2_channel_reserve_satoshis(
@@ -3848,17 +3850,15 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
38483850

38493851
/// Get the splice_ack message that can be sent in response to splice initiation.
38503852
#[cfg(splicing)]
3851-
pub fn get_splice_ack(&mut self, our_funding_contribution_satoshis: i64) -> Result<msgs::SpliceAck, ChannelError> {
3852-
// TODO(splicing): checks
3853-
3853+
pub fn get_splice_ack(&self, our_funding_contribution_satoshis: i64) -> msgs::SpliceAck {
38543854
// Reuse the existing funding pubkey, in spite of the channel value changing
38553855
let funding_pubkey = self.get_holder_pubkeys().funding_pubkey;
3856-
Ok(msgs::SpliceAck {
3856+
msgs::SpliceAck {
38573857
channel_id: self.channel_id,
38583858
funding_contribution_satoshis: our_funding_contribution_satoshis,
38593859
funding_pubkey,
38603860
require_confirmed_inputs: None,
3861-
})
3861+
}
38623862
}
38633863
}
38643864

@@ -7439,23 +7439,118 @@ impl<SP: Deref> Channel<SP> where
74397439
}
74407440
}
74417441

7442+
/// Initiate splicing
74427443
#[cfg(splicing)]
7443-
pub fn splice_init<ES: Deref, L: Deref>(
7444-
&mut self, our_funding_contribution_satoshis: i64,
7445-
_signer_provider: &SP, _entropy_source: &ES, _holder_node_id: PublicKey, _logger: &L
7446-
)
7447-
-> Result<msgs::SpliceAck, ChannelError>
7448-
where ES::Target: EntropySource, L::Target: Logger
7449-
{
7450-
if !self.context.is_outbound() {
7451-
// TODO(splicing): Apply start of splice (splice_start)
7444+
pub fn splice_channel(&mut self, our_funding_contribution_satoshis: i64,
7445+
funding_feerate_perkw: u32, locktime: u32,
7446+
) -> Result<msgs::SpliceInit, ChannelError> {
7447+
// Check if a splice has been initiated already.
7448+
// Note: this could be handled more nicely, and support multiple outstanding splice's, the incoming splice_ack matters anyways.
7449+
if let Some(splice_info) = &self.context.pending_splice_pre {
7450+
return Err(ChannelError::Warn(format!(
7451+
"Channel has already a splice pending, contribution {}", splice_info.our_funding_contribution
7452+
)));
7453+
}
74527454

7453-
let splice_ack_msg = self.context.get_splice_ack(our_funding_contribution_satoshis)?;
7454-
// TODO(splicing): start interactive funding negotiation
7455-
Ok(splice_ack_msg)
7456-
} else {
7457-
Err(ChannelError::Warn("Internal consistency error: splice_init on inbound channel".into()))
7455+
if !matches!(self.context.channel_state, ChannelState::ChannelReady(_)) {
7456+
return Err(ChannelError::Warn(format!("Cannot initiate splicing, as channel is not Ready")));
7457+
}
7458+
7459+
let pre_channel_value = self.context.get_value_satoshis();
7460+
// Sanity check: capacity cannot decrease below 0
7461+
if (pre_channel_value as i64).saturating_add(our_funding_contribution_satoshis) < 0 {
7462+
return Err(ChannelError::Warn(format!(
7463+
"Post-splicing channel value cannot be negative. It was {} + {}",
7464+
pre_channel_value, our_funding_contribution_satoshis
7465+
)));
7466+
}
7467+
7468+
if our_funding_contribution_satoshis < 0 {
7469+
return Err(ChannelError::Warn(format!(
7470+
"TODO(splicing): Splice-out not supported, only splice in, contribution {}",
7471+
our_funding_contribution_satoshis,
7472+
)));
7473+
}
7474+
7475+
// Note: post-splice channel value is not yet known at this point, counterpary contribution is not known
7476+
// (Cannot test for miminum required post-splice channel value)
7477+
7478+
self.context.pending_splice_pre = Some(PendingSpliceInfoPre {
7479+
our_funding_contribution: our_funding_contribution_satoshis,
7480+
});
7481+
7482+
let msg = self.context.get_splice_init(our_funding_contribution_satoshis, funding_feerate_perkw, locktime);
7483+
Ok(msg)
7484+
}
7485+
7486+
/// Handle splice_init
7487+
#[cfg(splicing)]
7488+
pub fn splice_init(
7489+
&mut self, their_funding_contribution_satoshis: i64, our_funding_contribution_satoshis: i64,
7490+
) -> Result<msgs::SpliceAck, ChannelError> {
7491+
// Check if a splice has been initiated already.
7492+
// Note: this could be handled more nicely, and support multiple outstanding splice's, the incoming splice_ack matters anyways.
7493+
if let Some(splice_info) = &self.context.pending_splice_pre {
7494+
return Err(ChannelError::Warn(format!(
7495+
"Channel has already a splice pending, contribution {}", splice_info.our_funding_contribution,
7496+
)));
7497+
}
7498+
7499+
if !matches!(self.context.channel_state, ChannelState::ChannelReady(_)) {
7500+
return Err(ChannelError::Warn(format!("Splicing requested on a channel that is not Ready")));
7501+
}
7502+
7503+
let pre_channel_value = self.context.get_value_satoshis();
7504+
// Sanity check: capacity cannot decrease below 0
7505+
if (pre_channel_value as i64)
7506+
.saturating_add(their_funding_contribution_satoshis)
7507+
.saturating_add(our_funding_contribution_satoshis) < 0
7508+
{
7509+
return Err(ChannelError::Warn(format!(
7510+
"Post-splicing channel value cannot be negative. It was {} + {} + {}",
7511+
pre_channel_value, their_funding_contribution_satoshis, our_funding_contribution_satoshis,
7512+
)));
7513+
}
7514+
7515+
if their_funding_contribution_satoshis.saturating_add(our_funding_contribution_satoshis) < 0 {
7516+
return Err(ChannelError::Warn(format!(
7517+
"Splice-out not supported, only splice in, relative {} + {}",
7518+
their_funding_contribution_satoshis, our_funding_contribution_satoshis,
7519+
)));
74587520
}
7521+
7522+
let post_channel_value = PendingSpliceInfoPre::compute_post_value(pre_channel_value, their_funding_contribution_satoshis, our_funding_contribution_satoshis);
7523+
7524+
// Early check for reserve requirement, assuming maximum balance of full channel value
7525+
// This will also be checked later at tx_complete
7526+
let _res = self.context.check_balance_meets_reserve_requirements(post_channel_value, post_channel_value)?;
7527+
7528+
// TODO(splicing): Apply start of splice (splice_start)
7529+
7530+
let splice_ack_msg = self.context.get_splice_ack(our_funding_contribution_satoshis);
7531+
// TODO(splicing): start interactive funding negotiation
7532+
Ok(splice_ack_msg)
7533+
}
7534+
7535+
/// Handle splice_ack
7536+
#[cfg(splicing)]
7537+
pub fn splice_ack(
7538+
&mut self, their_funding_contribution_satoshis: i64,
7539+
) -> Result<(), ChannelError> {
7540+
// check if splice is pending
7541+
let pending_splice = if let Some(pending_splice) = &self.context.pending_splice_pre {
7542+
pending_splice
7543+
} else {
7544+
return Err(ChannelError::Warn(format!("Channel is not in pending splice")));
7545+
};
7546+
7547+
let pre_channel_value = self.context.get_value_satoshis();
7548+
let post_channel_value = PendingSpliceInfoPre::compute_post_value(pre_channel_value, pending_splice.our_funding_contribution, their_funding_contribution_satoshis);
7549+
7550+
// Early check for reserve requirement, assuming maximum balance of full channel value
7551+
// This will also be checked later at tx_complete
7552+
let _res = self.context.check_balance_meets_reserve_requirements(post_channel_value, post_channel_value)?;
7553+
Ok(())
74597554
}
74607555

74617556
// Send stuff to our remote peers:

lightning/src/ln/channelmanager.rs

+14-80
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,6 @@ use crate::ln::inbound_payment;
5252
use crate::ln::types::ChannelId;
5353
use crate::types::payment::{PaymentHash, PaymentPreimage, PaymentSecret};
5454
use crate::ln::channel::{self, Channel, ChannelPhase, ChannelError, ChannelUpdateStatus, ShutdownResult, UpdateFulfillCommitFetch, OutboundV1Channel, InboundV1Channel, WithChannelContext};
55-
#[cfg(splicing)]
56-
use crate::ln::channel::PendingSpliceInfoPre;
5755
use crate::ln::channel_state::ChannelDetails;
5856
use crate::types::features::{Bolt12InvoiceFeatures, ChannelFeatures, ChannelTypeFeatures, InitFeatures, NodeFeatures};
5957
#[cfg(any(feature = "_test_utils", test))]
@@ -4126,41 +4124,17 @@ where
41264124
let peer_state = &mut *peer_state_lock;
41274125

41284126
// Look for the channel
4129-
match peer_state.channel_by_id.entry(channel_id.clone()) {
4127+
match peer_state.channel_by_id.entry(*channel_id) {
41304128
hash_map::Entry::Occupied(mut chan_phase_entry) => {
41314129
if let ChannelPhase::Funded(chan) = chan_phase_entry.get_mut() {
4132-
let pre_channel_value = chan.context.get_value_satoshis();
4133-
// Sanity check: capacity cannot decrease below 0
4134-
if (pre_channel_value as i64).saturating_add(our_funding_contribution_satoshis) < 0 {
4135-
return Err(APIError::APIMisuseError {
4136-
err: format!(
4137-
"Post-splicing channel value cannot be negative. It was {} - -{}",
4138-
pre_channel_value, our_funding_contribution_satoshis
4139-
)
4140-
});
4141-
}
4142-
4143-
if our_funding_contribution_satoshis < 0 {
4144-
return Err(APIError::APIMisuseError {
4130+
let msg = match chan.splice_channel(our_funding_contribution_satoshis, funding_feerate_perkw, locktime) {
4131+
Ok(msg) => msg,
4132+
Err(err) => return Err(APIError::APIMisuseError {
41454133
err: format!(
4146-
"TODO(splicing): Splice-out not supported, only splice in, contribution {}, channel_id {}",
4147-
our_funding_contribution_satoshis, channel_id
4134+
"Cannot initiate Splicing, {}, channel ID {}", err, channel_id
41484135
)
4149-
});
4150-
}
4151-
4152-
// Note: post-splice channel value is not yet known at this point, counterpary contribution is not known
4153-
// (Cannot test for miminum required post-splice channel value)
4154-
4155-
if chan.context.pending_splice_pre.is_some() {
4156-
return Err(APIError::ChannelUnavailable { err: format!("Channel has already a splice pending, channel id {}", channel_id) });
4157-
}
4158-
4159-
chan.context.pending_splice_pre = Some(PendingSpliceInfoPre {
4160-
our_funding_contribution: our_funding_contribution_satoshis,
4161-
});
4162-
4163-
let msg = chan.context.get_splice_init(our_funding_contribution_satoshis, funding_feerate_perkw, locktime);
4136+
}),
4137+
};
41644138

41654139
peer_state.pending_msg_events.push(events::MessageSendEvent::SendSpliceInit {
41664140
node_id: *counterparty_node_id,
@@ -9038,38 +9012,7 @@ where
90389012
), msg.channel_id)),
90399013
hash_map::Entry::Occupied(mut chan_entry) => {
90409014
if let ChannelPhase::Funded(chan) = chan_entry.get_mut() {
9041-
let pre_channel_value = chan.context.get_value_satoshis();
9042-
// Sanity check: capacity cannot decrease below 0
9043-
if (pre_channel_value as i64).saturating_add(msg.funding_contribution_satoshis) < 0 {
9044-
return Err(MsgHandleErrInternal::send_err_msg_no_close(format!(
9045-
"Post-splicing channel value cannot be negative. It was {} - -{}", pre_channel_value, msg.funding_contribution_satoshis,
9046-
), msg.channel_id));
9047-
}
9048-
9049-
if msg.funding_contribution_satoshis < 0 {
9050-
return Err(MsgHandleErrInternal::send_err_msg_no_close(format!(
9051-
"Splice-out not supported, only splice in, relative {}", -msg.funding_contribution_satoshis,
9052-
), msg.channel_id));
9053-
}
9054-
9055-
let post_channel_value = PendingSpliceInfoPre::compute_post_value(pre_channel_value, msg.funding_contribution_satoshis, our_funding_contribution);
9056-
9057-
// Early check for reserve requirement, assuming maximum balance of full channel value
9058-
// This will also be checked later at tx_complete
9059-
let _res = chan.context.check_balance_meets_reserve_requirements(post_channel_value, post_channel_value)
9060-
.map_err(|e| MsgHandleErrInternal::from_chan_no_close(e, msg.channel_id))?;
9061-
9062-
// Check if a splice has been initiated already.
9063-
// Note: this could be handled more nicely, and support multiple outstanding splice's, the incoming splice_ack matters anyways.
9064-
if let Some(splice_info) = &chan.context.pending_splice_pre {
9065-
return Err(MsgHandleErrInternal::send_err_msg_no_close(format!(
9066-
"Channel has already a splice pending, channel id {}, contribution {}",
9067-
msg.channel_id, splice_info.our_funding_contribution,
9068-
), msg.channel_id));
9069-
}
9070-
9071-
// TODO(splicing): Below step should come after channel phase change (after re-adding to channel map)
9072-
match chan.splice_init(our_funding_contribution, &self.signer_provider, &self.entropy_source, self.get_our_node_id(), &self.logger) {
9015+
match chan.splice_init(msg.funding_contribution_satoshis, our_funding_contribution) {
90739016
Ok(splice_ack_msg) => {
90749017
peer_state.pending_msg_events.push(events::MessageSendEvent::SendSpliceAck {
90759018
node_id: *counterparty_node_id,
@@ -9117,21 +9060,12 @@ where
91179060
), msg.channel_id)),
91189061
hash_map::Entry::Occupied(mut chan) => {
91199062
if let ChannelPhase::Funded(chan) = chan.get_mut() {
9120-
// check if splice is pending
9121-
let pending_splice = if let Some(pending_splice) = &chan.context.pending_splice_pre {
9122-
// Note: this is incomplete (their funding contribution is not set)
9123-
pending_splice.clone()
9124-
} else {
9125-
return Err(MsgHandleErrInternal::send_err_msg_no_close("Channel is not in pending splice".to_owned(), msg.channel_id));
9126-
};
9127-
9128-
let pre_channel_value = chan.context.get_value_satoshis();
9129-
let post_channel_value = PendingSpliceInfoPre::compute_post_value(pre_channel_value, pending_splice.our_funding_contribution, msg.funding_contribution_satoshis);
9130-
9131-
// Early check for reserve requirement, assuming maximum balance of full channel value
9132-
// This will also be checked later at tx_complete
9133-
let _res = chan.context.check_balance_meets_reserve_requirements(post_channel_value, post_channel_value)
9134-
.map_err(|e| MsgHandleErrInternal::from_chan_no_close(e, msg.channel_id))?;
9063+
match chan.splice_ack(msg.funding_contribution_satoshis) {
9064+
Ok(_) => {}
9065+
Err(err) => {
9066+
return Err(MsgHandleErrInternal::from_chan_no_close(err, msg.channel_id));
9067+
}
9068+
}
91359069
} else {
91369070
return Err(MsgHandleErrInternal::send_err_msg_no_close("Channel is not funded, cannot splice".to_owned(), msg.channel_id));
91379071
}

0 commit comments

Comments
 (0)