Skip to content

Commit cd372c9

Browse files
committed
Move checks&handling to Channel, cleanup, (review)
1 parent 874dd69 commit cd372c9

File tree

2 files changed

+131
-104
lines changed

2 files changed

+131
-104
lines changed

lightning/src/ln/channel.rs

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

11911191
#[cfg(splicing)]
11921192
impl PendingSpliceInfoPre {
1193+
#[inline]
11931194
fn add_checked(base: u64, delta: i64) -> u64 {
11941195
if delta >= 0 {
11951196
base.saturating_add(delta as u64)
@@ -1241,7 +1242,7 @@ pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
12411242

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

12461247
latest_monitor_update_id: u64,
12471248

@@ -3648,7 +3649,8 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
36483649
}
36493650

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

41754177
/// Get the splice_ack message that can be sent in response to splice initiation.
41764178
#[cfg(splicing)]
4177-
pub fn get_splice_ack(&mut self, our_funding_contribution_satoshis: i64) -> Result<msgs::SpliceAck, ChannelError> {
4178-
// TODO(splicing): checks
4179-
4179+
pub fn get_splice_ack(&self, our_funding_contribution_satoshis: i64) -> msgs::SpliceAck {
41804180
// Reuse the existing funding pubkey, in spite of the channel value changing
41814181
let funding_pubkey = self.get_holder_pubkeys().funding_pubkey;
4182-
Ok(msgs::SpliceAck {
4182+
msgs::SpliceAck {
41834183
channel_id: self.channel_id,
41844184
funding_contribution_satoshis: our_funding_contribution_satoshis,
41854185
funding_pubkey,
41864186
require_confirmed_inputs: None,
4187-
})
4187+
}
41884188
}
41894189
}
41904190

@@ -7908,23 +7908,118 @@ impl<SP: Deref> Channel<SP> where
79087908
}
79097909
}
79107910

7911+
/// Initiate splicing
79117912
#[cfg(splicing)]
7912-
pub fn splice_init<ES: Deref, L: Deref>(
7913-
&mut self, our_funding_contribution_satoshis: i64,
7914-
_signer_provider: &SP, _entropy_source: &ES, _holder_node_id: PublicKey, _logger: &L
7915-
)
7916-
-> Result<msgs::SpliceAck, ChannelError>
7917-
where ES::Target: EntropySource, L::Target: Logger
7918-
{
7919-
if !self.context.is_outbound() {
7920-
// TODO(splicing): Apply start of splice (splice_start)
7913+
pub fn splice_channel(&mut self, our_funding_contribution_satoshis: i64,
7914+
funding_feerate_perkw: u32, locktime: u32,
7915+
) -> Result<msgs::SpliceInit, ChannelError> {
7916+
// Check if a splice has been initiated already.
7917+
// Note: this could be handled more nicely, and support multiple outstanding splice's, the incoming splice_ack matters anyways.
7918+
if let Some(splice_info) = &self.context.pending_splice_pre {
7919+
return Err(ChannelError::Warn(format!(
7920+
"Channel has already a splice pending, contribution {}", splice_info.our_funding_contribution
7921+
)));
7922+
}
79217923

7922-
let splice_ack_msg = self.context.get_splice_ack(our_funding_contribution_satoshis)?;
7923-
// TODO(splicing): start interactive funding negotiation
7924-
Ok(splice_ack_msg)
7925-
} else {
7926-
Err(ChannelError::Warn("Internal consistency error: splice_init on inbound channel".into()))
7924+
if !matches!(self.context.channel_state, ChannelState::ChannelReady(_)) {
7925+
return Err(ChannelError::Warn(format!("Cannot initiate splicing, as channel is not Ready")));
7926+
}
7927+
7928+
let pre_channel_value = self.context.get_value_satoshis();
7929+
// Sanity check: capacity cannot decrease below 0
7930+
if (pre_channel_value as i64).saturating_add(our_funding_contribution_satoshis) < 0 {
7931+
return Err(ChannelError::Warn(format!(
7932+
"Post-splicing channel value cannot be negative. It was {} + {}",
7933+
pre_channel_value, our_funding_contribution_satoshis
7934+
)));
7935+
}
7936+
7937+
if our_funding_contribution_satoshis < 0 {
7938+
return Err(ChannelError::Warn(format!(
7939+
"TODO(splicing): Splice-out not supported, only splice in, contribution {}",
7940+
our_funding_contribution_satoshis,
7941+
)));
7942+
}
7943+
7944+
// Note: post-splice channel value is not yet known at this point, counterpary contribution is not known
7945+
// (Cannot test for miminum required post-splice channel value)
7946+
7947+
self.context.pending_splice_pre = Some(PendingSpliceInfoPre {
7948+
our_funding_contribution: our_funding_contribution_satoshis,
7949+
});
7950+
7951+
let msg = self.context.get_splice_init(our_funding_contribution_satoshis, funding_feerate_perkw, locktime);
7952+
Ok(msg)
7953+
}
7954+
7955+
/// Handle splice_init
7956+
#[cfg(splicing)]
7957+
pub fn splice_init(
7958+
&mut self, their_funding_contribution_satoshis: i64, our_funding_contribution_satoshis: i64,
7959+
) -> Result<msgs::SpliceAck, ChannelError> {
7960+
// Check if a splice has been initiated already.
7961+
// Note: this could be handled more nicely, and support multiple outstanding splice's, the incoming splice_ack matters anyways.
7962+
if let Some(splice_info) = &self.context.pending_splice_pre {
7963+
return Err(ChannelError::Warn(format!(
7964+
"Channel has already a splice pending, contribution {}", splice_info.our_funding_contribution,
7965+
)));
7966+
}
7967+
7968+
if !matches!(self.context.channel_state, ChannelState::ChannelReady(_)) {
7969+
return Err(ChannelError::Warn(format!("Splicing requested on a channel that is not Ready")));
7970+
}
7971+
7972+
let pre_channel_value = self.context.get_value_satoshis();
7973+
// Sanity check: capacity cannot decrease below 0
7974+
if (pre_channel_value as i64)
7975+
.saturating_add(their_funding_contribution_satoshis)
7976+
.saturating_add(our_funding_contribution_satoshis) < 0
7977+
{
7978+
return Err(ChannelError::Warn(format!(
7979+
"Post-splicing channel value cannot be negative. It was {} + {} + {}",
7980+
pre_channel_value, their_funding_contribution_satoshis, our_funding_contribution_satoshis,
7981+
)));
7982+
}
7983+
7984+
if their_funding_contribution_satoshis.saturating_add(our_funding_contribution_satoshis) < 0 {
7985+
return Err(ChannelError::Warn(format!(
7986+
"Splice-out not supported, only splice in, relative {} + {}",
7987+
their_funding_contribution_satoshis, our_funding_contribution_satoshis,
7988+
)));
79277989
}
7990+
7991+
let post_channel_value = PendingSpliceInfoPre::compute_post_value(pre_channel_value, their_funding_contribution_satoshis, our_funding_contribution_satoshis);
7992+
7993+
// Early check for reserve requirement, assuming maximum balance of full channel value
7994+
// This will also be checked later at tx_complete
7995+
let _res = self.context.check_balance_meets_reserve_requirements(post_channel_value, post_channel_value)?;
7996+
7997+
// TODO(splicing): Apply start of splice (splice_start)
7998+
7999+
let splice_ack_msg = self.context.get_splice_ack(our_funding_contribution_satoshis);
8000+
// TODO(splicing): start interactive funding negotiation
8001+
Ok(splice_ack_msg)
8002+
}
8003+
8004+
/// Handle splice_ack
8005+
#[cfg(splicing)]
8006+
pub fn splice_ack(
8007+
&mut self, their_funding_contribution_satoshis: i64,
8008+
) -> Result<(), ChannelError> {
8009+
// check if splice is pending
8010+
let pending_splice = if let Some(pending_splice) = &self.context.pending_splice_pre {
8011+
pending_splice
8012+
} else {
8013+
return Err(ChannelError::Warn(format!("Channel is not in pending splice")));
8014+
};
8015+
8016+
let pre_channel_value = self.context.get_value_satoshis();
8017+
let post_channel_value = PendingSpliceInfoPre::compute_post_value(pre_channel_value, pending_splice.our_funding_contribution, their_funding_contribution_satoshis);
8018+
8019+
// Early check for reserve requirement, assuming maximum balance of full channel value
8020+
// This will also be checked later at tx_complete
8021+
let _res = self.context.check_balance_meets_reserve_requirements(post_channel_value, post_channel_value)?;
8022+
Ok(())
79288023
}
79298024

79308025
// Send stuff to our remote peers:

lightning/src/ln/channelmanager.rs

+14-82
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,6 @@ use bitcoin::hash_types::{BlockHash, Txid};
3131
use bitcoin::secp256k1::{SecretKey,PublicKey};
3232
use bitcoin::secp256k1::Secp256k1;
3333
use bitcoin::{secp256k1, Sequence, Weight};
34-
#[cfg(splicing)]
35-
use bitcoin::TxIn;
3634

3735
use crate::events::FundingInfo;
3836
use crate::blinded_path::message::{AsyncPaymentsContext, MessageContext, OffersContext};
@@ -51,8 +49,6 @@ use crate::ln::inbound_payment;
5149
use crate::ln::types::ChannelId;
5250
use crate::types::payment::{PaymentHash, PaymentPreimage, PaymentSecret};
5351
use crate::ln::channel::{self, Channel, ChannelPhase, ChannelError, ChannelUpdateStatus, ShutdownResult, UpdateFulfillCommitFetch, OutboundV1Channel, InboundV1Channel, WithChannelContext, InboundV2Channel, InteractivelyFunded as _};
54-
#[cfg(splicing)]
55-
use crate::ln::channel::PendingSpliceInfoPre;
5652
use crate::ln::channel_state::ChannelDetails;
5753
use crate::types::features::{Bolt12InvoiceFeatures, ChannelFeatures, ChannelTypeFeatures, InitFeatures, NodeFeatures};
5854
#[cfg(any(feature = "_test_utils", test))]
@@ -4138,41 +4134,17 @@ where
41384134
let peer_state = &mut *peer_state_lock;
41394135

41404136
// Look for the channel
4141-
match peer_state.channel_by_id.entry(channel_id.clone()) {
4137+
match peer_state.channel_by_id.entry(*channel_id) {
41424138
hash_map::Entry::Occupied(mut chan_phase_entry) => {
41434139
if let ChannelPhase::Funded(chan) = chan_phase_entry.get_mut() {
4144-
let pre_channel_value = chan.context.get_value_satoshis();
4145-
// Sanity check: capacity cannot decrease below 0
4146-
if (pre_channel_value as i64).saturating_add(our_funding_contribution_satoshis) < 0 {
4147-
return Err(APIError::APIMisuseError {
4140+
let msg = match chan.splice_channel(our_funding_contribution_satoshis, funding_feerate_perkw, locktime) {
4141+
Ok(msg) => msg,
4142+
Err(err) => return Err(APIError::APIMisuseError {
41484143
err: format!(
4149-
"Post-splicing channel value cannot be negative. It was {} - -{}",
4150-
pre_channel_value, our_funding_contribution_satoshis
4144+
"Cannot initiate Splicing, {}, channel ID {}", err, channel_id
41514145
)
4152-
});
4153-
}
4154-
4155-
if our_funding_contribution_satoshis < 0 {
4156-
return Err(APIError::APIMisuseError {
4157-
err: format!(
4158-
"TODO(splicing): Splice-out not supported, only splice in, contribution {}, channel_id {}",
4159-
our_funding_contribution_satoshis, channel_id
4160-
)
4161-
});
4162-
}
4163-
4164-
// Note: post-splice channel value is not yet known at this point, counterpary contribution is not known
4165-
// (Cannot test for miminum required post-splice channel value)
4166-
4167-
if chan.context.pending_splice_pre.is_some() {
4168-
return Err(APIError::ChannelUnavailable { err: format!("Channel has already a splice pending, channel id {}", channel_id) });
4169-
}
4170-
4171-
chan.context.pending_splice_pre = Some(PendingSpliceInfoPre {
4172-
our_funding_contribution: our_funding_contribution_satoshis,
4173-
});
4174-
4175-
let msg = chan.context.get_splice_init(our_funding_contribution_satoshis, funding_feerate_perkw, locktime);
4146+
}),
4147+
};
41764148

41774149
peer_state.pending_msg_events.push(events::MessageSendEvent::SendSpliceInit {
41784150
node_id: *counterparty_node_id,
@@ -9404,38 +9376,7 @@ where
94049376
), msg.channel_id)),
94059377
hash_map::Entry::Occupied(mut chan_entry) => {
94069378
if let ChannelPhase::Funded(chan) = chan_entry.get_mut() {
9407-
let pre_channel_value = chan.context.get_value_satoshis();
9408-
// Sanity check: capacity cannot decrease below 0
9409-
if (pre_channel_value as i64).saturating_add(msg.funding_contribution_satoshis) < 0 {
9410-
return Err(MsgHandleErrInternal::send_err_msg_no_close(format!(
9411-
"Post-splicing channel value cannot be negative. It was {} - -{}", pre_channel_value, msg.funding_contribution_satoshis,
9412-
), msg.channel_id));
9413-
}
9414-
9415-
if msg.funding_contribution_satoshis < 0 {
9416-
return Err(MsgHandleErrInternal::send_err_msg_no_close(format!(
9417-
"Splice-out not supported, only splice in, relative {}", -msg.funding_contribution_satoshis,
9418-
), msg.channel_id));
9419-
}
9420-
9421-
let post_channel_value = PendingSpliceInfoPre::compute_post_value(pre_channel_value, msg.funding_contribution_satoshis, our_funding_contribution);
9422-
9423-
// Early check for reserve requirement, assuming maximum balance of full channel value
9424-
// This will also be checked later at tx_complete
9425-
let _res = chan.context.check_balance_meets_reserve_requirements(post_channel_value, post_channel_value)
9426-
.map_err(|e| MsgHandleErrInternal::from_chan_no_close(e, msg.channel_id))?;
9427-
9428-
// Check if a splice has been initiated already.
9429-
// Note: this could be handled more nicely, and support multiple outstanding splice's, the incoming splice_ack matters anyways.
9430-
if let Some(splice_info) = &chan.context.pending_splice_pre {
9431-
return Err(MsgHandleErrInternal::send_err_msg_no_close(format!(
9432-
"Channel has already a splice pending, channel id {}, contribution {}",
9433-
msg.channel_id, splice_info.our_funding_contribution,
9434-
), msg.channel_id));
9435-
}
9436-
9437-
// TODO(splicing): Below step should come after channel phase change (after re-adding to channel map)
9438-
match chan.splice_init(our_funding_contribution, &self.signer_provider, &self.entropy_source, self.get_our_node_id(), &self.logger) {
9379+
match chan.splice_init(msg.funding_contribution_satoshis, our_funding_contribution) {
94399380
Ok(splice_ack_msg) => {
94409381
peer_state.pending_msg_events.push(events::MessageSendEvent::SendSpliceAck {
94419382
node_id: *counterparty_node_id,
@@ -9483,21 +9424,12 @@ where
94839424
), msg.channel_id)),
94849425
hash_map::Entry::Occupied(mut chan) => {
94859426
if let ChannelPhase::Funded(chan) = chan.get_mut() {
9486-
// check if splice is pending
9487-
let pending_splice = if let Some(pending_splice) = &chan.context.pending_splice_pre {
9488-
// Note: this is incomplete (their funding contribution is not set)
9489-
pending_splice.clone()
9490-
} else {
9491-
return Err(MsgHandleErrInternal::send_err_msg_no_close("Channel is not in pending splice".to_owned(), msg.channel_id));
9492-
};
9493-
9494-
let pre_channel_value = chan.context.get_value_satoshis();
9495-
let post_channel_value = PendingSpliceInfoPre::compute_post_value(pre_channel_value, pending_splice.our_funding_contribution, msg.funding_contribution_satoshis);
9496-
9497-
// Early check for reserve requirement, assuming maximum balance of full channel value
9498-
// This will also be checked later at tx_complete
9499-
let _res = chan.context.check_balance_meets_reserve_requirements(post_channel_value, post_channel_value)
9500-
.map_err(|e| MsgHandleErrInternal::from_chan_no_close(e, msg.channel_id))?;
9427+
match chan.splice_ack(msg.funding_contribution_satoshis) {
9428+
Ok(_) => {}
9429+
Err(err) => {
9430+
return Err(MsgHandleErrInternal::from_chan_no_close(err, msg.channel_id));
9431+
}
9432+
}
95019433
} else {
95029434
return Err(MsgHandleErrInternal::send_err_msg_no_close("Channel is not funded, cannot splice".to_owned(), msg.channel_id));
95039435
}

0 commit comments

Comments
 (0)