Skip to content

Commit bb8cc16

Browse files
committed
Emit SpliceFailed upon disconnect while quiescent
Since quiescence is terminated upon disconnection, any outstanding splice negotiation should result in emitting a SpliceFailed event as long as we haven't reached FundingNegotiation::AwaitingSignatures. This may occur if we explicitly disconnect the peer (e.g., when failing to process splice_ack) or if the connection is lost..
1 parent cb9a396 commit bb8cc16

File tree

2 files changed

+102
-54
lines changed

2 files changed

+102
-54
lines changed

lightning/src/ln/channel.rs

Lines changed: 74 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1193,6 +1193,14 @@ pub(crate) struct ShutdownResult {
11931193
pub(crate) splice_funding_failed: Option<SpliceFundingFailed>,
11941194
}
11951195

1196+
/// The result of a peer disconnection.
1197+
pub(crate) struct DisconnectResult {
1198+
pub(crate) is_resumable: bool,
1199+
/// If a splice was in progress when the channel was shut down, this contains
1200+
/// the splice funding information for emitting a SpliceFailed event.
1201+
pub(crate) splice_funding_failed: Option<SpliceFundingFailed>,
1202+
}
1203+
11961204
/// Tracks the transaction number, along with current and next commitment points.
11971205
/// This consolidates the logic to advance our commitment number and request new
11981206
/// commitment points from our signer.
@@ -1585,11 +1593,15 @@ where
15851593
/// Should be called when the peer is disconnected. Returns true if the channel can be resumed
15861594
/// when the peer reconnects (via [`Self::peer_connected_get_handshake`]). If not, the channel
15871595
/// must be immediately closed.
1588-
#[rustfmt::skip]
1589-
pub fn peer_disconnected_is_resumable<L: Deref>(&mut self, logger: &L) -> bool where L::Target: Logger {
1590-
match &mut self.phase {
1596+
pub fn peer_disconnected_is_resumable<L: Deref>(&mut self, logger: &L) -> DisconnectResult
1597+
where
1598+
L::Target: Logger,
1599+
{
1600+
let is_resumable = match &mut self.phase {
15911601
ChannelPhase::Undefined => unreachable!(),
1592-
ChannelPhase::Funded(chan) => chan.remove_uncommitted_htlcs_and_mark_paused(logger).is_ok(),
1602+
ChannelPhase::Funded(chan) => {
1603+
chan.remove_uncommitted_htlcs_and_mark_paused(logger).is_ok()
1604+
},
15931605
// If we get disconnected and haven't yet committed to a funding
15941606
// transaction, we can replay the `open_channel` on reconnection, so don't
15951607
// bother dropping the channel here. However, if we already committed to
@@ -1599,7 +1611,34 @@ where
15991611
ChannelPhase::UnfundedOutboundV1(chan) => chan.is_resumable(),
16001612
ChannelPhase::UnfundedInboundV1(_) => false,
16011613
ChannelPhase::UnfundedV2(_) => false,
1602-
}
1614+
};
1615+
1616+
let splice_funding_failed = if let ChannelPhase::Funded(chan) = &mut self.phase {
1617+
// Reset any quiescence-related state as it is implicitly terminated once disconnected.
1618+
if matches!(chan.context.channel_state, ChannelState::ChannelReady(_)) {
1619+
if chan.quiescent_action.is_some() {
1620+
// If we were trying to get quiescent, try again after reconnection.
1621+
chan.context.channel_state.set_awaiting_quiescence();
1622+
}
1623+
chan.context.channel_state.clear_local_stfu_sent();
1624+
chan.context.channel_state.clear_remote_stfu_sent();
1625+
if chan.should_reset_pending_splice_funding_negotiation().unwrap_or(true) {
1626+
// If we were in quiescence but a splice was never negotiated, or the negotiation
1627+
// failed due to disconnecting, we shouldn't be quiescent anymore upon reconnecting.
1628+
// If there was a pending splice negotiation that has failed due to disconnecting,
1629+
// we also take the opportunity to clean up our state.
1630+
chan.reset_pending_splice_state()
1631+
} else {
1632+
None
1633+
}
1634+
} else {
1635+
None
1636+
}
1637+
} else {
1638+
None
1639+
};
1640+
1641+
DisconnectResult { is_resumable, splice_funding_failed }
16031642
}
16041643

16051644
/// Should be called when the peer re-connects, returning an initial message which we should
@@ -6808,8 +6847,15 @@ where
68086847
}
68096848

68106849
pub fn force_shutdown(&mut self, closure_reason: ClosureReason) -> ShutdownResult {
6811-
let splice_funding_failed = self
6812-
.reset_pending_splice_state()
6850+
let splice_funding_failed = self.maybe_fail_splice_negotiation();
6851+
6852+
let mut shutdown_result = self.context.force_shutdown(&self.funding, closure_reason);
6853+
shutdown_result.splice_funding_failed = splice_funding_failed;
6854+
shutdown_result
6855+
}
6856+
6857+
fn maybe_fail_splice_negotiation(&mut self) -> Option<SpliceFundingFailed> {
6858+
self.reset_pending_splice_state()
68136859
.or_else(|| {
68146860
self.quiescent_action.take().and_then(|quiescent_action| match quiescent_action {
68156861
QuiescentAction::Splice(instructions) => {
@@ -6827,11 +6873,7 @@ where
68276873
None
68286874
},
68296875
})
6830-
});
6831-
6832-
let mut shutdown_result = self.context.force_shutdown(&self.funding, closure_reason);
6833-
shutdown_result.splice_funding_failed = splice_funding_failed;
6834-
shutdown_result
6876+
})
68356877
}
68366878

68376879
fn interactive_tx_constructor_mut(&mut self) -> Option<&mut InteractiveTxConstructor> {
@@ -9090,24 +9132,6 @@ where
90909132
}
90919133
}
90929134

9093-
// Reset any quiescence-related state as it is implicitly terminated once disconnected.
9094-
if matches!(self.context.channel_state, ChannelState::ChannelReady(_)) {
9095-
if self.quiescent_action.is_some() {
9096-
// If we were trying to get quiescent, try again after reconnection.
9097-
self.context.channel_state.set_awaiting_quiescence();
9098-
}
9099-
self.context.channel_state.clear_local_stfu_sent();
9100-
self.context.channel_state.clear_remote_stfu_sent();
9101-
if self.should_reset_pending_splice_funding_negotiation().unwrap_or(true) {
9102-
// If we were in quiescence but a splice was never negotiated, or the negotiation
9103-
// failed due to disconnecting, we shouldn't be quiescent anymore upon reconnecting.
9104-
// If there was a pending splice negotiation that has failed due to disconnecting,
9105-
// we also take the opportunity to clean up our state.
9106-
self.reset_pending_splice_state();
9107-
debug_assert!(!self.context.channel_state.is_quiescent());
9108-
}
9109-
}
9110-
91119135
self.context.channel_state.set_peer_disconnected();
91129136
log_trace!(logger, "Peer disconnection resulted in {} remote-announced HTLC drops on channel {}", inbound_drop_count, &self.context.channel_id());
91139137
Ok(())
@@ -11774,9 +11798,9 @@ where
1177411798
.map_err(|e| APIError::APIMisuseError { err: e.to_owned() })
1177511799
}
1177611800

11777-
fn send_splice_init(
11778-
&mut self, instructions: SpliceInstructions,
11779-
) -> Result<msgs::SpliceInit, String> {
11801+
fn send_splice_init(&mut self, instructions: SpliceInstructions) -> msgs::SpliceInit {
11802+
debug_assert!(self.pending_splice.is_none());
11803+
1178011804
let SpliceInstructions {
1178111805
adjusted_funding_contribution,
1178211806
our_funding_inputs,
@@ -11786,15 +11810,6 @@ where
1178611810
locktime,
1178711811
} = instructions;
1178811812

11789-
// Check if a splice has been initiated already.
11790-
// Note: only a single outstanding splice is supported (per spec)
11791-
if self.pending_splice.is_some() {
11792-
return Err(format!(
11793-
"Channel {} cannot be spliced, as it has already a splice pending",
11794-
self.context.channel_id(),
11795-
));
11796-
}
11797-
1179811813
let prev_funding_input = self.funding.to_splice_funding_input();
1179911814
let context = FundingNegotiationContext {
1180011815
is_initiator: true,
@@ -11818,14 +11833,14 @@ where
1181811833
let prev_funding_txid = self.funding.get_funding_txid();
1181911834
let funding_pubkey = self.context.holder_pubkeys(prev_funding_txid).funding_pubkey;
1182011835

11821-
Ok(msgs::SpliceInit {
11836+
msgs::SpliceInit {
1182211837
channel_id: self.context.channel_id,
1182311838
funding_contribution_satoshis: adjusted_funding_contribution.to_sat(),
1182411839
funding_feerate_per_kw,
1182511840
locktime,
1182611841
funding_pubkey,
1182711842
require_confirmed_inputs: None,
11828-
})
11843+
}
1182911844
}
1183011845

1183111846
/// Checks during handling splice_init
@@ -12970,10 +12985,21 @@ where
1297012985
"Internal Error: Didn't have anything to do after reaching quiescence".to_owned()
1297112986
));
1297212987
},
12973-
Some(QuiescentAction::Splice(_instructions)) => {
12974-
return self.send_splice_init(_instructions)
12975-
.map(|splice_init| Some(StfuResponse::SpliceInit(splice_init)))
12976-
.map_err(|e| ChannelError::WarnAndDisconnect(e.to_owned()));
12988+
Some(QuiescentAction::Splice(instructions)) => {
12989+
if self.pending_splice.is_some() {
12990+
debug_assert!(false);
12991+
self.quiescent_action = Some(QuiescentAction::Splice(instructions));
12992+
12993+
return Err(ChannelError::WarnAndDisconnect(
12994+
format!(
12995+
"Channel {} cannot be spliced as it already has a splice pending",
12996+
self.context.channel_id(),
12997+
),
12998+
));
12999+
}
13000+
13001+
let splice_init = self.send_splice_init(instructions);
13002+
return Ok(Some(StfuResponse::SpliceInit(splice_init)));
1297713003
},
1297813004
#[cfg(any(test, fuzzing))]
1297913005
Some(QuiescentAction::DoNothing) => {

lightning/src/ln/channelmanager.rs

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,10 @@ use crate::ln::chan_utils::selected_commitment_sat_per_1000_weight;
5959
#[cfg(any(test, fuzzing))]
6060
use crate::ln::channel::QuiescentAction;
6161
use crate::ln::channel::{
62-
self, hold_time_since, Channel, ChannelError, ChannelUpdateStatus, FundedChannel,
63-
InboundV1Channel, OutboundV1Channel, PendingV2Channel, ReconnectionMsg, ShutdownResult,
64-
SpliceFundingFailed, StfuResponse, UpdateFulfillCommitFetch, WithChannelContext,
62+
self, hold_time_since, Channel, ChannelError, ChannelUpdateStatus, DisconnectResult,
63+
FundedChannel, InboundV1Channel, OutboundV1Channel, PendingV2Channel, ReconnectionMsg,
64+
ShutdownResult, SpliceFundingFailed, StfuResponse, UpdateFulfillCommitFetch,
65+
WithChannelContext,
6566
};
6667
use crate::ln::channel_state::ChannelDetails;
6768
use crate::ln::funding::SpliceContribution;
@@ -13447,8 +13448,8 @@ where
1344713448

1344813449
#[rustfmt::skip]
1344913450
fn peer_disconnected(&self, counterparty_node_id: PublicKey) {
13450-
let _persistence_guard = PersistenceNotifierGuard::optionally_notify(
13451-
self, || NotifyOption::SkipPersistHandleEvents);
13451+
let _persistence_guard = PersistenceNotifierGuard::optionally_notify(self, || {
13452+
let mut persist = NotifyOption::SkipPersistHandleEvents;
1345213453
let mut failed_channels: Vec<(Result<Infallible, _>, _)> = Vec::new();
1345313454
let mut per_peer_state = self.per_peer_state.write().unwrap();
1345413455
let remove_peer = {
@@ -13461,11 +13462,29 @@ where
1346113462
let mut peer_state_lock = peer_state_mutex.lock().unwrap();
1346213463
let peer_state = &mut *peer_state_lock;
1346313464
let pending_msg_events = &mut peer_state.pending_msg_events;
13465+
let pending_events = &mut self.pending_events.lock().unwrap();
1346413466
peer_state.channel_by_id.retain(|_, chan| {
1346513467
let logger = WithChannelContext::from(&self.logger, &chan.context(), None);
13466-
if chan.peer_disconnected_is_resumable(&&logger) {
13468+
let DisconnectResult { is_resumable, splice_funding_failed } =
13469+
chan.peer_disconnected_is_resumable(&&logger);
13470+
13471+
if let Some(splice_funding_failed) = splice_funding_failed {
13472+
pending_events.push_back((events::Event::SpliceFailed {
13473+
channel_id: chan.context().channel_id(),
13474+
counterparty_node_id,
13475+
user_channel_id: chan.context().get_user_id(),
13476+
funding_txo: splice_funding_failed.funding_txo,
13477+
channel_type: splice_funding_failed.channel_type,
13478+
contributed_inputs: splice_funding_failed.contributed_inputs,
13479+
contributed_outputs: splice_funding_failed.contributed_outputs,
13480+
}, None));
13481+
persist = NotifyOption::DoPersist;
13482+
}
13483+
13484+
if is_resumable {
1346713485
return true;
1346813486
}
13487+
1346913488
// Clean up for removal.
1347013489
let reason = ClosureReason::DisconnectedPeer;
1347113490
let err = ChannelError::Close((reason.to_string(), reason));
@@ -13548,6 +13567,9 @@ where
1354813567
for (err, counterparty_node_id) in failed_channels.drain(..) {
1354913568
let _ = handle_error!(self, err, counterparty_node_id);
1355013569
}
13570+
13571+
persist
13572+
});
1355113573
}
1355213574

1355313575
#[rustfmt::skip]

0 commit comments

Comments
 (0)