Skip to content

Commit c2c6fdf

Browse files
committed
Emit SpliceFailed event upon reload
Similarly to when a peer is disconnected, when a node is reload 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 3e83b55 commit c2c6fdf

File tree

4 files changed

+105
-8
lines changed

4 files changed

+105
-8
lines changed

lightning/src/ln/channel.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6707,6 +6707,13 @@ impl FundingNegotiationContext {
67076707
let contributed_outputs = self.our_funding_outputs;
67086708
(contributed_inputs, contributed_outputs)
67096709
}
6710+
6711+
fn to_contributed_inputs_and_outputs(&self) -> (Vec<bitcoin::OutPoint>, Vec<TxOut>) {
6712+
let contributed_inputs =
6713+
self.our_funding_inputs.iter().map(|input| input.utxo.outpoint).collect();
6714+
let contributed_outputs = self.our_funding_outputs.clone();
6715+
(contributed_inputs, contributed_outputs)
6716+
}
67106717
}
67116718

67126719
// Holder designates channel data owned for the benefit of the user client.
@@ -7004,6 +7011,49 @@ where
70047011
splice_funding_failed
70057012
}
70067013

7014+
pub(super) fn maybe_splice_funding_failed(&self) -> Option<SpliceFundingFailed> {
7015+
if !self.should_reset_pending_splice_funding_negotiation().unwrap_or(false) {
7016+
return None;
7017+
}
7018+
7019+
self.pending_splice
7020+
.as_ref()
7021+
.and_then(|pending_splice| pending_splice.funding_negotiation.as_ref())
7022+
.filter(|funding_negotiation| funding_negotiation.is_initiator())
7023+
.map(|funding_negotiation| {
7024+
let funding_txo = funding_negotiation
7025+
.as_funding()
7026+
.and_then(|funding| funding.get_funding_txo())
7027+
.map(|txo| txo.into_bitcoin_outpoint());
7028+
7029+
let channel_type = funding_negotiation
7030+
.as_funding()
7031+
.map(|funding| funding.get_channel_type().clone());
7032+
7033+
let (contributed_inputs, contributed_outputs) = match funding_negotiation {
7034+
FundingNegotiation::AwaitingAck { context } => {
7035+
context.to_contributed_inputs_and_outputs()
7036+
},
7037+
FundingNegotiation::ConstructingTransaction {
7038+
interactive_tx_constructor,
7039+
..
7040+
} => interactive_tx_constructor.to_contributed_inputs_and_outputs(),
7041+
FundingNegotiation::AwaitingSignatures { .. } => {
7042+
// FIXME Uncomment after rebase
7043+
//debug_assert!(false);
7044+
(Vec::new(), Vec::new())
7045+
},
7046+
};
7047+
7048+
SpliceFundingFailed {
7049+
funding_txo,
7050+
channel_type,
7051+
contributed_inputs,
7052+
contributed_outputs,
7053+
}
7054+
})
7055+
}
7056+
70077057
#[rustfmt::skip]
70087058
fn check_remote_fee<F: Deref, L: Deref>(
70097059
channel_type: &ChannelTypeFeatures, fee_estimator: &LowerBoundedFeeEstimator<F>,

lightning/src/ln/channelmanager.rs

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16092,6 +16092,32 @@ where
1609216092
htlc_onion_fields.push(&payment.onion_fields);
1609316093
}
1609416094

16095+
// Since some FundingNegotiation variants are not persisted, any splice in such state must
16096+
// be failed upon reload. However, as the necessary information for the SpliceFailed event
16097+
// is not persisted, the event itself needs to be persisted even though it hasn't been
16098+
// emitted yet.
16099+
let mut splice_failed_events = Vec::new();
16100+
for (_, peer_state_mutex) in per_peer_state.iter() {
16101+
let mut peer_state_lock = peer_state_mutex.lock().unwrap();
16102+
let peer_state = &mut *peer_state_lock;
16103+
for chan in peer_state.channel_by_id.values().filter_map(Channel::as_funded) {
16104+
if let Some(splice_funding_failed) = chan.maybe_splice_funding_failed() {
16105+
splice_failed_events.push((
16106+
events::Event::SpliceFailed {
16107+
channel_id: chan.context.channel_id(),
16108+
counterparty_node_id: chan.context.get_counterparty_node_id(),
16109+
user_channel_id: chan.context.get_user_id(),
16110+
abandoned_funding_txo: splice_funding_failed.funding_txo,
16111+
channel_type: splice_funding_failed.channel_type,
16112+
contributed_inputs: splice_funding_failed.contributed_inputs,
16113+
contributed_outputs: splice_funding_failed.contributed_outputs,
16114+
},
16115+
None,
16116+
));
16117+
}
16118+
}
16119+
}
16120+
1609516121
let mut monitor_update_blocked_actions_per_peer = None;
1609616122
let mut peer_states = Vec::new();
1609716123
for (_, peer_state_mutex) in per_peer_state.iter() {
@@ -16123,17 +16149,19 @@ where
1612316149
}
1612416150

1612516151
let events = self.pending_events.lock().unwrap();
16152+
let events_iter = || events.iter().chain(splice_failed_events.iter());
16153+
let event_count = events.len() + splice_failed_events.len();
1612616154
// LDK versions prior to 0.0.115 don't support post-event actions, thus if there's no
1612716155
// actions at all, skip writing the required TLV. Otherwise, pre-0.0.115 versions will
1612816156
// refuse to read the new ChannelManager.
16129-
let events_not_backwards_compatible = events.iter().any(|(_, action)| action.is_some());
16157+
let events_not_backwards_compatible = events_iter().any(|(_, action)| action.is_some());
1613016158
if events_not_backwards_compatible {
1613116159
// If we're gonna write a even TLV that will overwrite our events anyway we might as
1613216160
// well save the space and not write any events here.
1613316161
0u64.write(writer)?;
1613416162
} else {
16135-
(events.len() as u64).write(writer)?;
16136-
for (event, _) in events.iter() {
16163+
(event_count as u64).write(writer)?;
16164+
for (event, _) in events_iter() {
1613716165
event.write(writer)?;
1613816166
}
1613916167
}
@@ -16219,6 +16247,9 @@ where
1621916247
}
1622016248
}
1622116249

16250+
let events_iter = events_not_backwards_compatible
16251+
.then(|| crate::util::ser::Iterable(events_iter()));
16252+
1622216253
write_tlv_fields!(writer, {
1622316254
(1, pending_outbound_payments_no_retry, required),
1622416255
(2, pending_intercepted_htlcs, option),
@@ -16227,7 +16258,7 @@ where
1622716258
(5, self.our_network_pubkey, required),
1622816259
(6, monitor_update_blocked_actions_per_peer, option),
1622916260
(7, self.fake_scid_rand_bytes, required),
16230-
(8, if events_not_backwards_compatible { Some(&*events) } else { None }, option),
16261+
(8, events_iter, option),
1623116262
(9, htlc_purposes, required_vec),
1623216263
(10, legacy_in_flight_monitor_updates, option),
1623316264
(11, self.probing_cookie_secret, required),

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
@@ -430,10 +430,10 @@ fn do_test_splice_state_reset_on_disconnect(reload: bool) {
430430
} else {
431431
nodes[0].node.peer_disconnected(node_id_1);
432432
nodes[1].node.peer_disconnected(node_id_0);
433-
434-
let _event = get_event!(nodes[0], Event::SpliceFailed);
435433
}
436434

435+
let _event = get_event!(nodes[0], Event::SpliceFailed);
436+
437437
let mut reconnect_args = ReconnectArgs::new(&nodes[0], &nodes[1]);
438438
reconnect_args.send_channel_ready = (true, true);
439439
reconnect_nodes(reconnect_args);
@@ -497,10 +497,10 @@ fn do_test_splice_state_reset_on_disconnect(reload: bool) {
497497
} else {
498498
nodes[0].node.peer_disconnected(node_id_1);
499499
nodes[1].node.peer_disconnected(node_id_0);
500-
501-
let _event = get_event!(nodes[0], Event::SpliceFailed);
502500
}
503501

502+
let _event = get_event!(nodes[0], Event::SpliceFailed);
503+
504504
let mut reconnect_args = ReconnectArgs::new(&nodes[0], &nodes[1]);
505505
reconnect_args.send_channel_ready = (true, true);
506506
reconnect_nodes(reconnect_args);

0 commit comments

Comments
 (0)