Skip to content

Commit cfe2a1e

Browse files
authored
Merge pull request #4120 from wpaulino/reset-splice-state
Wipe splice state upon failed interactive funding construction
2 parents 4ae8b3a + 6d2b110 commit cfe2a1e

File tree

3 files changed

+372
-38
lines changed

3 files changed

+372
-38
lines changed

lightning/src/ln/channel.rs

Lines changed: 122 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1686,27 +1686,20 @@ where
16861686
let logger = WithChannelContext::from(logger, &self.context(), None);
16871687
log_info!(logger, "Failed interactive transaction negotiation: {reason}");
16881688

1689-
let _interactive_tx_constructor = match &mut self.phase {
1689+
match &mut self.phase {
16901690
ChannelPhase::Undefined => unreachable!(),
1691-
ChannelPhase::UnfundedOutboundV1(_) | ChannelPhase::UnfundedInboundV1(_) => None,
1691+
ChannelPhase::UnfundedOutboundV1(_) | ChannelPhase::UnfundedInboundV1(_) => {},
16921692
ChannelPhase::UnfundedV2(pending_v2_channel) => {
1693-
pending_v2_channel.interactive_tx_constructor.take()
1693+
pending_v2_channel.interactive_tx_constructor.take();
1694+
},
1695+
ChannelPhase::Funded(funded_channel) => {
1696+
if funded_channel.should_reset_pending_splice_funding_negotiation().unwrap_or(true)
1697+
{
1698+
funded_channel.reset_pending_splice_state();
1699+
} else {
1700+
debug_assert!(false, "We should never fail an interactive funding negotiation once we're exchanging tx_signatures");
1701+
}
16941702
},
1695-
ChannelPhase::Funded(funded_channel) => funded_channel
1696-
.pending_splice
1697-
.as_mut()
1698-
.and_then(|pending_splice| pending_splice.funding_negotiation.take())
1699-
.and_then(|funding_negotiation| {
1700-
if let FundingNegotiation::ConstructingTransaction {
1701-
interactive_tx_constructor,
1702-
..
1703-
} = funding_negotiation
1704-
{
1705-
Some(interactive_tx_constructor)
1706-
} else {
1707-
None
1708-
}
1709-
}),
17101703
};
17111704

17121705
reason.into_tx_abort_msg(self.context().channel_id)
@@ -1818,11 +1811,11 @@ where
18181811
where
18191812
L::Target: Logger,
18201813
{
1821-
// This checks for and resets the interactive negotiation state by `take()`ing it from the channel.
1822-
// The existence of the `tx_constructor` indicates that we have not moved into the signing
1823-
// phase for this interactively constructed transaction and hence we have not exchanged
1824-
// `tx_signatures`. Either way, we never close the channel upon receiving a `tx_abort`:
1825-
// https://github.com/lightning/bolts/blob/247e83d/02-peer-protocol.md?plain=1#L574-L576
1814+
// If we have not sent a `tx_abort` message for this negotiation previously, we need to echo
1815+
// back a tx_abort message according to the spec:
1816+
// https://github.com/lightning/bolts/blob/247e83d/02-peer-protocol.md?plain=1#L560-L561
1817+
// For rationale why we echo back `tx_abort`:
1818+
// https://github.com/lightning/bolts/blob/247e83d/02-peer-protocol.md?plain=1#L578-L580
18261819
let should_ack = match &mut self.phase {
18271820
ChannelPhase::Undefined => unreachable!(),
18281821
ChannelPhase::UnfundedOutboundV1(_) | ChannelPhase::UnfundedInboundV1(_) => {
@@ -1832,19 +1825,27 @@ where
18321825
ChannelPhase::UnfundedV2(pending_v2_channel) => {
18331826
pending_v2_channel.interactive_tx_constructor.take().is_some()
18341827
},
1835-
ChannelPhase::Funded(funded_channel) => funded_channel
1836-
.pending_splice
1837-
.as_mut()
1838-
.and_then(|pending_splice| pending_splice.funding_negotiation.take())
1839-
.is_some(),
1828+
ChannelPhase::Funded(funded_channel) => {
1829+
if let Some(should_reset) =
1830+
funded_channel.should_reset_pending_splice_funding_negotiation()
1831+
{
1832+
if should_reset {
1833+
// We may have still tracked the pending funding negotiation state, so we
1834+
// should ack with our own `tx_abort`.
1835+
funded_channel.reset_pending_splice_state()
1836+
} else {
1837+
return Err(ChannelError::close(
1838+
"Received tx_abort while awaiting tx_signatures exchange".to_owned(),
1839+
));
1840+
}
1841+
} else {
1842+
// We were not tracking the pending funding negotiation state anymore, likely
1843+
// due to a disconnection or already having sent our own `tx_abort`.
1844+
false
1845+
}
1846+
},
18401847
};
18411848

1842-
// NOTE: Since at this point we have not sent a `tx_abort` message for this negotiation
1843-
// previously (tx_constructor was `Some`), we need to echo back a tx_abort message according
1844-
// to the spec:
1845-
// https://github.com/lightning/bolts/blob/247e83d/02-peer-protocol.md?plain=1#L560-L561
1846-
// For rationale why we echo back `tx_abort`:
1847-
// https://github.com/lightning/bolts/blob/247e83d/02-peer-protocol.md?plain=1#L578-L580
18481849
Ok(should_ack.then(|| {
18491850
let logger = WithChannelContext::from(logger, &self.context(), None);
18501851
let reason =
@@ -2554,6 +2555,15 @@ impl FundingNegotiation {
25542555
}
25552556

25562557
impl PendingFunding {
2558+
fn can_abandon_funding_negotiation(&self) -> bool {
2559+
self.funding_negotiation
2560+
.as_ref()
2561+
.map(|funding_negotiation| {
2562+
!matches!(funding_negotiation, FundingNegotiation::AwaitingSignatures { .. })
2563+
})
2564+
.unwrap_or(true)
2565+
}
2566+
25572567
fn check_get_splice_locked<SP: Deref>(
25582568
&mut self, context: &ChannelContext<SP>, confirmed_funding_index: usize, height: u32,
25592569
) -> Option<msgs::SpliceLocked>
@@ -6739,6 +6749,45 @@ where
67396749
)
67406750
}
67416751

6752+
/// Returns `None` if there is no [`FundedChannel::pending_splice`], otherwise a boolean
6753+
/// indicating whether we should reset the splice's [`PendingFunding::funding_negotiation`].
6754+
fn should_reset_pending_splice_funding_negotiation(&self) -> Option<bool> {
6755+
self.pending_splice.as_ref().map(|pending_splice| {
6756+
if pending_splice.can_abandon_funding_negotiation() {
6757+
true
6758+
} else {
6759+
self.context
6760+
.interactive_tx_signing_session
6761+
.as_ref()
6762+
.map(|signing_session| !signing_session.has_received_commitment_signed())
6763+
.unwrap_or_else(|| {
6764+
debug_assert!(false);
6765+
false
6766+
})
6767+
}
6768+
})
6769+
}
6770+
6771+
fn should_reset_pending_splice_state(&self) -> bool {
6772+
self.should_reset_pending_splice_funding_negotiation().unwrap_or(true)
6773+
&& self.pending_funding().is_empty()
6774+
}
6775+
6776+
fn reset_pending_splice_state(&mut self) -> bool {
6777+
debug_assert!(self.should_reset_pending_splice_funding_negotiation().unwrap_or(true));
6778+
self.context.channel_state.clear_quiescent();
6779+
self.context.interactive_tx_signing_session.take();
6780+
let has_funding_negotiation = self
6781+
.pending_splice
6782+
.as_mut()
6783+
.and_then(|pending_splice| pending_splice.funding_negotiation.take())
6784+
.is_some();
6785+
if self.should_reset_pending_splice_state() {
6786+
self.pending_splice.take();
6787+
}
6788+
has_funding_negotiation
6789+
}
6790+
67426791
#[rustfmt::skip]
67436792
fn check_remote_fee<F: Deref, L: Deref>(
67446793
channel_type: &ChannelTypeFeatures, fee_estimator: &LowerBoundedFeeEstimator<F>,
@@ -8864,7 +8913,14 @@ where
88648913
}
88658914
self.context.channel_state.clear_local_stfu_sent();
88668915
self.context.channel_state.clear_remote_stfu_sent();
8867-
self.context.channel_state.clear_quiescent();
8916+
if self.should_reset_pending_splice_funding_negotiation().unwrap_or(true) {
8917+
// If we were in quiescence but a splice was never negotiated, or the negotiation
8918+
// failed due to disconnecting, we shouldn't be quiescent anymore upon reconnecting.
8919+
// If there was a pending splice negotiation that has failed due to disconnecting,
8920+
// we also take the opportunity to clean up our state.
8921+
self.reset_pending_splice_state();
8922+
debug_assert!(!self.context.channel_state.is_quiescent());
8923+
}
88688924
}
88698925

88708926
self.context.channel_state.set_peer_disconnected();
@@ -13896,7 +13952,12 @@ where
1389613952
}
1389713953
channel_state.clear_local_stfu_sent();
1389813954
channel_state.clear_remote_stfu_sent();
13899-
channel_state.clear_quiescent();
13955+
if self.should_reset_pending_splice_funding_negotiation().unwrap_or(true) {
13956+
// If we were in quiescence but a splice was never negotiated, or the
13957+
// negotiation failed due to disconnecting, we shouldn't be quiescent
13958+
// anymore upon reconnecting.
13959+
channel_state.clear_quiescent();
13960+
}
1390013961
},
1390113962
ChannelState::FundingNegotiated(_)
1390213963
if self.context.interactive_tx_signing_session.is_some() => {},
@@ -14259,6 +14320,20 @@ where
1425914320
let holder_commitment_point_next = self.holder_commitment_point.next_point();
1426014321
let holder_commitment_point_pending_next = self.holder_commitment_point.pending_next_point;
1426114322

14323+
let interactive_tx_signing_session =
14324+
if self.should_reset_pending_splice_funding_negotiation().unwrap_or(false) {
14325+
None
14326+
} else {
14327+
self.context.interactive_tx_signing_session.as_ref()
14328+
};
14329+
let pending_splice = if self.should_reset_pending_splice_state() {
14330+
None
14331+
} else {
14332+
// We don't have to worry about resetting the pending `FundingNegotiation` because we
14333+
// can only read `FundingNegotiation::AwaitingSignatures` variants anyway.
14334+
self.pending_splice.as_ref()
14335+
};
14336+
1426214337
write_tlv_fields!(writer, {
1426314338
(0, self.context.announcement_sigs, option),
1426414339
// minimum_depth and counterparty_selected_channel_reserve_satoshis used to have a
@@ -14302,12 +14377,12 @@ where
1430214377
(53, funding_tx_broadcast_safe_event_emitted, option), // Added in 0.0.124
1430314378
(55, removed_htlc_attribution_data, optional_vec), // Added in 0.2
1430414379
(57, holding_cell_attribution_data, optional_vec), // Added in 0.2
14305-
(58, self.context.interactive_tx_signing_session, option), // Added in 0.2
14380+
(58, interactive_tx_signing_session, option), // Added in 0.2
1430614381
(59, self.funding.minimum_depth_override, option), // Added in 0.2
1430714382
(60, self.context.historical_scids, optional_vec), // Added in 0.2
1430814383
(61, fulfill_attribution_data, optional_vec), // Added in 0.2
1430914384
(63, holder_commitment_point_current, option), // Added in 0.2
14310-
(64, self.pending_splice, option), // Added in 0.2
14385+
(64, pending_splice, option), // Added in 0.2
1431114386
(65, self.quiescent_action, option), // Added in 0.2
1431214387
(67, pending_outbound_held_htlc_flags, optional_vec), // Added in 0.2
1431314388
(69, holding_cell_held_htlc_flags, optional_vec), // Added in 0.2
@@ -14971,6 +15046,15 @@ where
1497115046
}
1497215047
};
1497315048

15049+
if let Some(funding_negotiation) = pending_splice
15050+
.as_ref()
15051+
.and_then(|pending_splice| pending_splice.funding_negotiation.as_ref())
15052+
{
15053+
if !matches!(funding_negotiation, FundingNegotiation::AwaitingSignatures { .. }) {
15054+
return Err(DecodeError::InvalidValue);
15055+
}
15056+
}
15057+
1497415058
Ok(FundedChannel {
1497515059
funding: FundingScope {
1497615060
value_to_self_msat,

lightning/src/ln/quiescence_tests.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use crate::ln::functional_test_utils::*;
77
use crate::ln::msgs;
88
use crate::ln::msgs::{BaseMessageHandler, ChannelMessageHandler, ErrorAction, MessageSendEvent};
99
use crate::util::errors::APIError;
10+
use crate::util::ser::Writeable;
1011
use crate::util::test_channel_signer::SignerOp;
1112

1213
#[test]
@@ -699,3 +700,62 @@ fn test_quiescence_during_disconnection() {
699700
do_test_quiescence_during_disconnection(false, true);
700701
do_test_quiescence_during_disconnection(true, true);
701702
}
703+
704+
#[test]
705+
fn test_quiescence_termination_on_disconnect() {
706+
do_test_quiescence_termination_on_disconnect(false);
707+
do_test_quiescence_termination_on_disconnect(true);
708+
}
709+
710+
fn do_test_quiescence_termination_on_disconnect(reload: bool) {
711+
// Tests that we terminate quiescence on disconnect if there's no quiescence protocol taking place.
712+
let chanmon_cfgs = create_chanmon_cfgs(2);
713+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
714+
let (persister_0a, persister_1a);
715+
let (chain_monitor_0a, chain_monitor_1a);
716+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
717+
let (node_0a, node_1a);
718+
let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
719+
720+
let node_id_0 = nodes[0].node.get_our_node_id();
721+
let node_id_1 = nodes[1].node.get_our_node_id();
722+
723+
let (_, _, channel_id, _) = create_announced_chan_between_nodes(&nodes, 0, 1);
724+
725+
nodes[0].node.maybe_propose_quiescence(&node_id_1, &channel_id).unwrap();
726+
727+
let stfu = get_event_msg!(nodes[0], MessageSendEvent::SendStfu, node_id_1);
728+
nodes[1].node.handle_stfu(node_id_0, &stfu);
729+
let stfu = get_event_msg!(nodes[1], MessageSendEvent::SendStfu, node_id_0);
730+
nodes[0].node.handle_stfu(node_id_1, &stfu);
731+
732+
if reload {
733+
let encoded_monitor_0 = get_monitor!(nodes[0], channel_id).encode();
734+
reload_node!(
735+
nodes[0],
736+
&nodes[0].node.encode(),
737+
&[&encoded_monitor_0],
738+
persister_0a,
739+
chain_monitor_0a,
740+
node_0a
741+
);
742+
let encoded_monitor_1 = get_monitor!(nodes[1], channel_id).encode();
743+
reload_node!(
744+
nodes[1],
745+
&nodes[1].node.encode(),
746+
&[&encoded_monitor_1],
747+
persister_1a,
748+
chain_monitor_1a,
749+
node_1a
750+
);
751+
} else {
752+
nodes[0].node.peer_disconnected(node_id_1);
753+
nodes[1].node.peer_disconnected(node_id_0);
754+
}
755+
756+
let mut reconnect_args = ReconnectArgs::new(&nodes[0], &nodes[1]);
757+
reconnect_args.send_channel_ready = (true, true);
758+
reconnect_nodes(reconnect_args);
759+
760+
send_payment(&nodes[0], &[&nodes[1]], 1_000_000);
761+
}

0 commit comments

Comments
 (0)