Skip to content

Commit 40a2522

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 71cfb67 commit 40a2522

File tree

4 files changed

+99
-5
lines changed

4 files changed

+99
-5
lines changed

lightning/src/ln/channel.rs

Lines changed: 49 additions & 0 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.
@@ -7022,6 +7029,48 @@ where
70227029
splice_funding_failed
70237030
}
70247031

7032+
pub(super) fn maybe_splice_funding_failed(&self) -> Option<SpliceFundingFailed> {
7033+
if !self.should_reset_pending_splice_state() {
7034+
return None;
7035+
}
7036+
7037+
self.pending_splice
7038+
.as_ref()
7039+
.and_then(|pending_splice| pending_splice.funding_negotiation.as_ref())
7040+
.filter(|funding_negotiation| funding_negotiation.is_initiator())
7041+
.map(|funding_negotiation| {
7042+
let funding_txo = funding_negotiation
7043+
.as_funding()
7044+
.and_then(|funding| funding.get_funding_txo())
7045+
.map(|txo| txo.into_bitcoin_outpoint());
7046+
7047+
let channel_type = funding_negotiation
7048+
.as_funding()
7049+
.map(|funding| funding.get_channel_type().clone());
7050+
7051+
let (contributed_inputs, contributed_outputs) = match funding_negotiation {
7052+
FundingNegotiation::AwaitingAck { context } => {
7053+
context.to_contributed_inputs_and_outputs()
7054+
},
7055+
FundingNegotiation::ConstructingTransaction {
7056+
interactive_tx_constructor,
7057+
..
7058+
} => interactive_tx_constructor.to_contributed_inputs_and_outputs(),
7059+
FundingNegotiation::AwaitingSignatures { .. } => {
7060+
debug_assert!(false);
7061+
(Vec::new(), Vec::new())
7062+
},
7063+
};
7064+
7065+
SpliceFundingFailed {
7066+
funding_txo,
7067+
channel_type,
7068+
contributed_inputs,
7069+
contributed_outputs,
7070+
}
7071+
})
7072+
}
7073+
70257074
#[rustfmt::skip]
70267075
fn check_remote_fee<F: Deref, L: Deref>(
70277076
channel_type: &ChannelTypeFeatures, fee_estimator: &LowerBoundedFeeEstimator<F>,

lightning/src/ln/channelmanager.rs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16098,6 +16098,32 @@ where
1609816098
htlc_onion_fields.push(&payment.onion_fields);
1609916099
}
1610016100

16101+
// Since some FundingNegotiation variants are not persisted, any splice in such state must
16102+
// be failed upon reload. However, as the necessary information for the SpliceFailed event
16103+
// is not persisted, the event itself needs to be persisted even though it hasn't been
16104+
// emitted yet. These are removed after the events are written.
16105+
let event_count = self.pending_events.lock().unwrap().len();
16106+
for (_, peer_state_mutex) in per_peer_state.iter() {
16107+
let mut peer_state_lock = peer_state_mutex.lock().unwrap();
16108+
let peer_state = &mut *peer_state_lock;
16109+
for chan in peer_state.channel_by_id.values().filter_map(Channel::as_funded) {
16110+
if let Some(splice_funding_failed) = chan.maybe_splice_funding_failed() {
16111+
self.pending_events.lock().unwrap().push_back((
16112+
events::Event::SpliceFailed {
16113+
channel_id: chan.context.channel_id(),
16114+
counterparty_node_id: chan.context.get_counterparty_node_id(),
16115+
user_channel_id: chan.context.get_user_id(),
16116+
abandoned_funding_txo: splice_funding_failed.funding_txo,
16117+
channel_type: splice_funding_failed.channel_type,
16118+
contributed_inputs: splice_funding_failed.contributed_inputs,
16119+
contributed_outputs: splice_funding_failed.contributed_outputs,
16120+
},
16121+
None,
16122+
));
16123+
}
16124+
}
16125+
}
16126+
1610116127
let mut monitor_update_blocked_actions_per_peer = None;
1610216128
let mut peer_states = Vec::new();
1610316129
for (_, peer_state_mutex) in per_peer_state.iter() {
@@ -16128,7 +16154,7 @@ where
1612816154
}
1612916155
}
1613016156

16131-
let events = self.pending_events.lock().unwrap();
16157+
let mut events = self.pending_events.lock().unwrap();
1613216158
// LDK versions prior to 0.0.115 don't support post-event actions, thus if there's no
1613316159
// actions at all, skip writing the required TLV. Otherwise, pre-0.0.115 versions will
1613416160
// refuse to read the new ChannelManager.
@@ -16245,6 +16271,9 @@ where
1624516271
(21, WithoutLength(&self.flow.writeable_async_receive_offer_cache()), required),
1624616272
});
1624716273

16274+
// Remove the SpliceFailed events added earlier.
16275+
events.truncate(event_count);
16276+
1624816277
Ok(())
1624916278
}
1625016279
}

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)