Skip to content

Commit f744403

Browse files
committed
Emit SpliceFailed event upon reload
Similarly to when a peer is disconnected, when a node is reloaded any splice that hasn't reaching FundingNegotiation::AwaitingSignatures will be reset. This should produce a SpliceFailed event. However, since other FundingNegotiation variants are not persisted, the data to produced the SpliceFailed event upon reload is lost. Therefore, opportunistically persist a SpliceFailed event for these cases such that it is available upon reload.
1 parent 35c9567 commit f744403

File tree

4 files changed

+112
-41
lines changed

4 files changed

+112
-41
lines changed

lightning/src/ln/channel.rs

Lines changed: 63 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -6732,6 +6732,13 @@ impl FundingNegotiationContext {
67326732
let contributed_outputs = self.our_funding_outputs;
67336733
(contributed_inputs, contributed_outputs)
67346734
}
6735+
6736+
fn to_contributed_inputs_and_outputs(&self) -> (Vec<bitcoin::OutPoint>, Vec<TxOut>) {
6737+
let contributed_inputs =
6738+
self.our_funding_inputs.iter().map(|input| input.utxo.outpoint).collect();
6739+
let contributed_outputs = self.our_funding_outputs.clone();
6740+
(contributed_inputs, contributed_outputs)
6741+
}
67356742
}
67366743

67376744
// Holder designates channel data owned for the benefit of the user client.
@@ -6865,6 +6872,45 @@ pub struct SpliceFundingFailed {
68656872
pub contributed_outputs: Vec<bitcoin::TxOut>,
68666873
}
68676874

6875+
macro_rules! maybe_create_splice_funding_failed {
6876+
($pending_splice: expr, $get: ident, $contributed_inputs_and_outputs: ident) => {{
6877+
$pending_splice
6878+
.and_then(|pending_splice| pending_splice.funding_negotiation.$get())
6879+
.filter(|funding_negotiation| funding_negotiation.is_initiator())
6880+
.map(|funding_negotiation| {
6881+
let funding_txo = funding_negotiation
6882+
.as_funding()
6883+
.and_then(|funding| funding.get_funding_txo())
6884+
.map(|txo| txo.into_bitcoin_outpoint());
6885+
6886+
let channel_type = funding_negotiation
6887+
.as_funding()
6888+
.map(|funding| funding.get_channel_type().clone());
6889+
6890+
let (contributed_inputs, contributed_outputs) = match funding_negotiation {
6891+
FundingNegotiation::AwaitingAck { context } => {
6892+
context.$contributed_inputs_and_outputs()
6893+
},
6894+
FundingNegotiation::ConstructingTransaction {
6895+
interactive_tx_constructor,
6896+
..
6897+
} => interactive_tx_constructor.$contributed_inputs_and_outputs(),
6898+
FundingNegotiation::AwaitingSignatures { .. } => {
6899+
debug_assert!(false);
6900+
(Vec::new(), Vec::new())
6901+
},
6902+
};
6903+
6904+
SpliceFundingFailed {
6905+
funding_txo,
6906+
channel_type,
6907+
contributed_inputs,
6908+
contributed_outputs,
6909+
}
6910+
})
6911+
}};
6912+
}
6913+
68686914
pub struct SpliceFundingPromotion {
68696915
pub funding_txo: OutPoint,
68706916
pub monitor_update: Option<ChannelMonitorUpdate>,
@@ -6977,42 +7023,11 @@ where
69777023
debug_assert!(self.context.interactive_tx_signing_session.is_none());
69787024
self.context.channel_state.clear_quiescent();
69797025

6980-
let splice_funding_failed = self
6981-
.pending_splice
6982-
.as_mut()
6983-
.and_then(|pending_splice| pending_splice.funding_negotiation.take())
6984-
.filter(|funding_negotiation| funding_negotiation.is_initiator())
6985-
.map(|funding_negotiation| {
6986-
let funding_txo = funding_negotiation
6987-
.as_funding()
6988-
.and_then(|funding| funding.get_funding_txo())
6989-
.map(|txo| txo.into_bitcoin_outpoint());
6990-
6991-
let channel_type = funding_negotiation
6992-
.as_funding()
6993-
.map(|funding| funding.get_channel_type().clone());
6994-
6995-
let (contributed_inputs, contributed_outputs) = match funding_negotiation {
6996-
FundingNegotiation::AwaitingAck { context } => {
6997-
context.into_contributed_inputs_and_outputs()
6998-
},
6999-
FundingNegotiation::ConstructingTransaction {
7000-
interactive_tx_constructor,
7001-
..
7002-
} => interactive_tx_constructor.into_contributed_inputs_and_outputs(),
7003-
FundingNegotiation::AwaitingSignatures { .. } => {
7004-
debug_assert!(false);
7005-
(Vec::new(), Vec::new())
7006-
},
7007-
};
7008-
7009-
SpliceFundingFailed {
7010-
funding_txo,
7011-
channel_type,
7012-
contributed_inputs,
7013-
contributed_outputs,
7014-
}
7015-
});
7026+
let splice_funding_failed = maybe_create_splice_funding_failed!(
7027+
self.pending_splice.as_mut(),
7028+
take,
7029+
into_contributed_inputs_and_outputs
7030+
);
70167031

70177032
if self.pending_funding().is_empty() {
70187033
self.pending_splice.take();
@@ -7021,6 +7036,18 @@ where
70217036
splice_funding_failed
70227037
}
70237038

7039+
pub(super) fn maybe_splice_funding_failed(&self) -> Option<SpliceFundingFailed> {
7040+
if !self.should_reset_pending_splice_state() {
7041+
return None;
7042+
}
7043+
7044+
maybe_create_splice_funding_failed!(
7045+
self.pending_splice.as_ref(),
7046+
as_ref,
7047+
to_contributed_inputs_and_outputs
7048+
)
7049+
}
7050+
70247051
#[rustfmt::skip]
70257052
fn check_remote_fee<F: Deref, L: Deref>(
70267053
channel_type: &ChannelTypeFeatures, fee_estimator: &LowerBoundedFeeEstimator<F>,

lightning/src/ln/channelmanager.rs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16134,7 +16134,32 @@ where
1613416134
}
1613516135
}
1613616136

16137-
let events = self.pending_events.lock().unwrap();
16137+
16138+
// Since some FundingNegotiation variants are not persisted, any splice in such state must
16139+
// be failed upon reload. However, as the necessary information for the SpliceFailed event
16140+
// is not persisted, the event itself needs to be persisted even though it hasn't been
16141+
// emitted yet. These are removed after the events are written.
16142+
let mut events = self.pending_events.lock().unwrap();
16143+
let event_count = events.len();
16144+
for peer_state in peer_states.iter() {
16145+
for chan in peer_state.channel_by_id.values().filter_map(Channel::as_funded) {
16146+
if let Some(splice_funding_failed) = chan.maybe_splice_funding_failed() {
16147+
events.push_back((
16148+
events::Event::SpliceFailed {
16149+
channel_id: chan.context.channel_id(),
16150+
counterparty_node_id: chan.context.get_counterparty_node_id(),
16151+
user_channel_id: chan.context.get_user_id(),
16152+
abandoned_funding_txo: splice_funding_failed.funding_txo,
16153+
channel_type: splice_funding_failed.channel_type,
16154+
contributed_inputs: splice_funding_failed.contributed_inputs,
16155+
contributed_outputs: splice_funding_failed.contributed_outputs,
16156+
},
16157+
None,
16158+
));
16159+
}
16160+
}
16161+
}
16162+
1613816163
// LDK versions prior to 0.0.115 don't support post-event actions, thus if there's no
1613916164
// actions at all, skip writing the required TLV. Otherwise, pre-0.0.115 versions will
1614016165
// refuse to read the new ChannelManager.
@@ -16251,6 +16276,9 @@ where
1625116276
(21, WithoutLength(&self.flow.writeable_async_receive_offer_cache()), required),
1625216277
});
1625316278

16279+
// Remove the SpliceFailed events added earlier.
16280+
events.truncate(event_count);
16281+
1625416282
Ok(())
1625516283
}
1625616284
}

lightning/src/ln/interactivetxs.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2100,6 +2100,22 @@ impl InteractiveTxConstructor {
21002100
(contributed_inputs, contributed_outputs)
21012101
}
21022102

2103+
pub(super) fn to_contributed_inputs_and_outputs(&self) -> (Vec<BitcoinOutPoint>, Vec<TxOut>) {
2104+
let contributed_inputs = self
2105+
.inputs_to_contribute
2106+
.iter()
2107+
.filter(|(_, input)| !input.is_shared())
2108+
.map(|(_, input)| input.tx_in().previous_output)
2109+
.collect();
2110+
let contributed_outputs = self
2111+
.outputs_to_contribute
2112+
.iter()
2113+
.filter(|(_, output)| !output.is_shared())
2114+
.map(|(_, output)| output.tx_out().clone())
2115+
.collect();
2116+
(contributed_inputs, contributed_outputs)
2117+
}
2118+
21032119
pub fn is_initiator(&self) -> bool {
21042120
self.is_initiator
21052121
}

lightning/src/ln/splicing_tests.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -431,10 +431,10 @@ fn do_test_splice_state_reset_on_disconnect(reload: bool) {
431431
} else {
432432
nodes[0].node.peer_disconnected(node_id_1);
433433
nodes[1].node.peer_disconnected(node_id_0);
434-
435-
let _event = get_event!(nodes[0], Event::SpliceFailed);
436434
}
437435

436+
let _event = get_event!(nodes[0], Event::SpliceFailed);
437+
438438
let mut reconnect_args = ReconnectArgs::new(&nodes[0], &nodes[1]);
439439
reconnect_args.send_channel_ready = (true, true);
440440
reconnect_args.send_announcement_sigs = (true, true);
@@ -490,10 +490,10 @@ fn do_test_splice_state_reset_on_disconnect(reload: bool) {
490490
} else {
491491
nodes[0].node.peer_disconnected(node_id_1);
492492
nodes[1].node.peer_disconnected(node_id_0);
493-
494-
let _event = get_event!(nodes[0], Event::SpliceFailed);
495493
}
496494

495+
let _event = get_event!(nodes[0], Event::SpliceFailed);
496+
497497
let mut reconnect_args = ReconnectArgs::new(&nodes[0], &nodes[1]);
498498
reconnect_args.send_channel_ready = (true, true);
499499
reconnect_args.send_announcement_sigs = (true, true);

0 commit comments

Comments
 (0)