Skip to content

Commit 39e0b7d

Browse files
committed
Construct ShutdownResult as a struct in Channel
This refactors ShutdownResult as follows: - Makes ShutdownResult a struct instead of a tuple to represent individual results that need to be handled. This recently also includes funding batch closure propagations. - Makes Channel solely responsible for constructing ShutdownResult as it should own all channel-specific logic.
1 parent f3ebf5c commit 39e0b7d

File tree

2 files changed

+70
-55
lines changed

2 files changed

+70
-55
lines changed

lightning/src/ln/channel.rs

+49-29
Original file line numberDiff line numberDiff line change
@@ -542,18 +542,16 @@ pub(super) struct ReestablishResponses {
542542
pub shutdown_msg: Option<msgs::Shutdown>,
543543
}
544544

545-
/// The return type of `force_shutdown`
546-
///
547-
/// Contains a tuple with the following:
548-
/// - An optional (counterparty_node_id, funding_txo, [`ChannelMonitorUpdate`]) tuple
549-
/// - A list of HTLCs to fail back in the form of the (source, payment hash, and this channel's
550-
/// counterparty_node_id and channel_id).
551-
/// - An optional transaction id identifying a corresponding batch funding transaction.
552-
pub(crate) type ShutdownResult = (
553-
Option<(PublicKey, OutPoint, ChannelMonitorUpdate)>,
554-
Vec<(HTLCSource, PaymentHash, PublicKey, ChannelId)>,
555-
Option<Txid>
556-
);
545+
/// The result of a shutdown that should be handled.
546+
pub(crate) struct ShutdownResult {
547+
/// A channel monitor update to apply.
548+
pub(crate) monitor_update: Option<(PublicKey, OutPoint, ChannelMonitorUpdate)>,
549+
/// A list of dropped outbound HTLCs that can safely be failed backwards immediately.
550+
pub(crate) dropped_outbound_htlcs: Vec<(HTLCSource, PaymentHash, PublicKey, ChannelId)>,
551+
/// An unbroadcasted batch funding transaction id. The closure of this channel should be
552+
/// propagated to the remainder of the batch.
553+
pub(crate) unbroadcasted_batch_funding_txid: Option<Txid>,
554+
}
557555

558556
/// If the majority of the channels funds are to the fundee and the initiator holds only just
559557
/// enough funds to cover their reserve value, channels are at risk of getting "stuck". Because the
@@ -2063,7 +2061,11 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
20632061

20642062
self.channel_state = ChannelState::ShutdownComplete as u32;
20652063
self.update_time_counter += 1;
2066-
(monitor_update, dropped_outbound_htlcs, unbroadcasted_batch_funding_txid)
2064+
ShutdownResult {
2065+
monitor_update,
2066+
dropped_outbound_htlcs,
2067+
unbroadcasted_batch_funding_txid,
2068+
}
20672069
}
20682070
}
20692071

@@ -4218,18 +4220,18 @@ impl<SP: Deref> Channel<SP> where
42184220

42194221
pub fn maybe_propose_closing_signed<F: Deref, L: Deref>(
42204222
&mut self, fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L)
4221-
-> Result<(Option<msgs::ClosingSigned>, Option<Transaction>), ChannelError>
4223+
-> Result<(Option<msgs::ClosingSigned>, Option<Transaction>, Option<ShutdownResult>), ChannelError>
42224224
where F::Target: FeeEstimator, L::Target: Logger
42234225
{
42244226
if self.context.last_sent_closing_fee.is_some() || !self.closing_negotiation_ready() {
4225-
return Ok((None, None));
4227+
return Ok((None, None, None));
42264228
}
42274229

42284230
if !self.context.is_outbound() {
42294231
if let Some(msg) = &self.context.pending_counterparty_closing_signed.take() {
42304232
return self.closing_signed(fee_estimator, &msg);
42314233
}
4232-
return Ok((None, None));
4234+
return Ok((None, None, None));
42334235
}
42344236

42354237
let (our_min_fee, our_max_fee) = self.calculate_closing_fee_limits(fee_estimator);
@@ -4254,7 +4256,7 @@ impl<SP: Deref> Channel<SP> where
42544256
min_fee_satoshis: our_min_fee,
42554257
max_fee_satoshis: our_max_fee,
42564258
}),
4257-
}), None))
4259+
}), None, None))
42584260
}
42594261
}
42604262
}
@@ -4403,7 +4405,7 @@ impl<SP: Deref> Channel<SP> where
44034405

44044406
pub fn closing_signed<F: Deref>(
44054407
&mut self, fee_estimator: &LowerBoundedFeeEstimator<F>, msg: &msgs::ClosingSigned)
4406-
-> Result<(Option<msgs::ClosingSigned>, Option<Transaction>), ChannelError>
4408+
-> Result<(Option<msgs::ClosingSigned>, Option<Transaction>, Option<ShutdownResult>), ChannelError>
44074409
where F::Target: FeeEstimator
44084410
{
44094411
if self.context.channel_state & BOTH_SIDES_SHUTDOWN_MASK != BOTH_SIDES_SHUTDOWN_MASK {
@@ -4425,7 +4427,7 @@ impl<SP: Deref> Channel<SP> where
44254427

44264428
if self.context.channel_state & ChannelState::MonitorUpdateInProgress as u32 != 0 {
44274429
self.context.pending_counterparty_closing_signed = Some(msg.clone());
4428-
return Ok((None, None));
4430+
return Ok((None, None, None));
44294431
}
44304432

44314433
let funding_redeemscript = self.context.get_funding_redeemscript();
@@ -4455,10 +4457,15 @@ impl<SP: Deref> Channel<SP> where
44554457
assert!(self.context.shutdown_scriptpubkey.is_some());
44564458
if let Some((last_fee, sig)) = self.context.last_sent_closing_fee {
44574459
if last_fee == msg.fee_satoshis {
4460+
let shutdown_result = ShutdownResult {
4461+
monitor_update: None,
4462+
dropped_outbound_htlcs: Vec::new(),
4463+
unbroadcasted_batch_funding_txid: self.context.unbroadcasted_batch_funding_txid(),
4464+
};
44584465
let tx = self.build_signed_closing_transaction(&mut closing_tx, &msg.signature, &sig);
44594466
self.context.channel_state = ChannelState::ShutdownComplete as u32;
44604467
self.context.update_time_counter += 1;
4461-
return Ok((None, Some(tx)));
4468+
return Ok((None, Some(tx), Some(shutdown_result)));
44624469
}
44634470
}
44644471

@@ -4477,13 +4484,19 @@ impl<SP: Deref> Channel<SP> where
44774484
let sig = ecdsa
44784485
.sign_closing_transaction(&closing_tx, &self.context.secp_ctx)
44794486
.map_err(|_| ChannelError::Close("External signer refused to sign closing transaction".to_owned()))?;
4480-
4481-
let signed_tx = if $new_fee == msg.fee_satoshis {
4487+
let (signed_tx, shutdown_result) = if $new_fee == msg.fee_satoshis {
4488+
let shutdown_result = ShutdownResult {
4489+
monitor_update: None,
4490+
dropped_outbound_htlcs: Vec::new(),
4491+
unbroadcasted_batch_funding_txid: self.context.unbroadcasted_batch_funding_txid(),
4492+
};
44824493
self.context.channel_state = ChannelState::ShutdownComplete as u32;
44834494
self.context.update_time_counter += 1;
44844495
let tx = self.build_signed_closing_transaction(&closing_tx, &msg.signature, &sig);
4485-
Some(tx)
4486-
} else { None };
4496+
(Some(tx), Some(shutdown_result))
4497+
} else {
4498+
(None, None)
4499+
};
44874500

44884501
self.context.last_sent_closing_fee = Some((used_fee, sig.clone()));
44894502
Ok((Some(msgs::ClosingSigned {
@@ -4494,7 +4507,7 @@ impl<SP: Deref> Channel<SP> where
44944507
min_fee_satoshis: our_min_fee,
44954508
max_fee_satoshis: our_max_fee,
44964509
}),
4497-
}), signed_tx))
4510+
}), signed_tx, shutdown_result))
44984511
}
44994512
}
45004513
}
@@ -5572,7 +5585,7 @@ impl<SP: Deref> Channel<SP> where
55725585
/// [`ChannelMonitorUpdate`] will be returned).
55735586
pub fn get_shutdown(&mut self, signer_provider: &SP, their_features: &InitFeatures,
55745587
target_feerate_sats_per_kw: Option<u32>, override_shutdown_script: Option<ShutdownScript>)
5575-
-> Result<(msgs::Shutdown, Option<ChannelMonitorUpdate>, Vec<(HTLCSource, PaymentHash)>), APIError>
5588+
-> Result<(msgs::Shutdown, Option<ChannelMonitorUpdate>, Vec<(HTLCSource, PaymentHash)>, Option<ShutdownResult>), APIError>
55765589
{
55775590
for htlc in self.context.pending_outbound_htlcs.iter() {
55785591
if let OutboundHTLCState::LocalAnnounced(_) = htlc.state {
@@ -5627,11 +5640,18 @@ impl<SP: Deref> Channel<SP> where
56275640

56285641
// From here on out, we may not fail!
56295642
self.context.target_closing_feerate_sats_per_kw = target_feerate_sats_per_kw;
5630-
if self.context.channel_state & !STATE_FLAGS < ChannelState::FundingSent as u32 {
5643+
let shutdown_result = if self.context.channel_state & !STATE_FLAGS < ChannelState::FundingSent as u32 {
5644+
let shutdown_result = ShutdownResult {
5645+
monitor_update: None,
5646+
dropped_outbound_htlcs: Vec::new(),
5647+
unbroadcasted_batch_funding_txid: self.context.unbroadcasted_batch_funding_txid(),
5648+
};
56315649
self.context.channel_state = ChannelState::ShutdownComplete as u32;
5650+
Some(shutdown_result)
56325651
} else {
56335652
self.context.channel_state |= ChannelState::LocalShutdownSent as u32;
5634-
}
5653+
None
5654+
};
56355655
self.context.update_time_counter += 1;
56365656

56375657
let monitor_update = if update_shutdown_script {
@@ -5667,7 +5687,7 @@ impl<SP: Deref> Channel<SP> where
56675687
debug_assert!(!self.is_shutdown() || monitor_update.is_none(),
56685688
"we can't both complete shutdown and return a monitor update");
56695689

5670-
Ok((shutdown, monitor_update, dropped_outbound_htlcs))
5690+
Ok((shutdown, monitor_update, dropped_outbound_htlcs, shutdown_result))
56715691
}
56725692

56735693
pub fn inflight_htlc_sources(&self) -> impl Iterator<Item=(&HTLCSource, &PaymentHash)> {

lightning/src/ln/channelmanager.rs

+21-26
Original file line numberDiff line numberDiff line change
@@ -2559,7 +2559,7 @@ where
25592559
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
25602560

25612561
let mut failed_htlcs: Vec<(HTLCSource, PaymentHash)>;
2562-
let mut shutdown_result = None;
2562+
let shutdown_result;
25632563
loop {
25642564
let per_peer_state = self.per_peer_state.read().unwrap();
25652565

@@ -2574,10 +2574,10 @@ where
25742574
if let ChannelPhase::Funded(chan) = chan_phase_entry.get_mut() {
25752575
let funding_txo_opt = chan.context.get_funding_txo();
25762576
let their_features = &peer_state.latest_features;
2577-
let unbroadcasted_batch_funding_txid = chan.context.unbroadcasted_batch_funding_txid();
2578-
let (shutdown_msg, mut monitor_update_opt, htlcs) =
2577+
let (shutdown_msg, mut monitor_update_opt, htlcs, local_shutdown_result) =
25792578
chan.get_shutdown(&self.signer_provider, their_features, target_feerate_sats_per_1000_weight, override_shutdown_script)?;
25802579
failed_htlcs = htlcs;
2580+
shutdown_result = local_shutdown_result;
25812581

25822582
// We can send the `shutdown` message before updating the `ChannelMonitor`
25832583
// here as we don't need the monitor update to complete until we send a
@@ -2605,7 +2605,6 @@ where
26052605
});
26062606
}
26072607
self.issue_channel_close_events(&chan.context, ClosureReason::HolderForceClosed);
2608-
shutdown_result = Some((None, Vec::new(), unbroadcasted_batch_funding_txid));
26092608
}
26102609
}
26112610
break;
@@ -2695,30 +2694,29 @@ where
26952694
self.close_channel_internal(channel_id, counterparty_node_id, target_feerate_sats_per_1000_weight, shutdown_script)
26962695
}
26972696

2698-
fn finish_close_channel(&self, shutdown_res: ShutdownResult) {
2697+
fn finish_close_channel(&self, mut shutdown_res: ShutdownResult) {
26992698
debug_assert_ne!(self.per_peer_state.held_by_thread(), LockHeldState::HeldByThread);
27002699
#[cfg(debug_assertions)]
27012700
for (_, peer) in self.per_peer_state.read().unwrap().iter() {
27022701
debug_assert_ne!(peer.held_by_thread(), LockHeldState::HeldByThread);
27032702
}
27042703

2705-
let (monitor_update_option, mut failed_htlcs, unbroadcasted_batch_funding_txid) = shutdown_res;
2706-
log_debug!(self.logger, "Finishing closure of channel with {} HTLCs to fail", failed_htlcs.len());
2707-
for htlc_source in failed_htlcs.drain(..) {
2704+
log_debug!(self.logger, "Finishing closure of channel with {} HTLCs to fail", shutdown_res.dropped_outbound_htlcs.len());
2705+
for htlc_source in shutdown_res.dropped_outbound_htlcs.drain(..) {
27082706
let (source, payment_hash, counterparty_node_id, channel_id) = htlc_source;
27092707
let reason = HTLCFailReason::from_failure_code(0x4000 | 8);
27102708
let receiver = HTLCDestination::NextHopChannel { node_id: Some(counterparty_node_id), channel_id };
27112709
self.fail_htlc_backwards_internal(&source, &payment_hash, &reason, receiver);
27122710
}
2713-
if let Some((_, funding_txo, monitor_update)) = monitor_update_option {
2711+
if let Some((_, funding_txo, monitor_update)) = shutdown_res.monitor_update {
27142712
// There isn't anything we can do if we get an update failure - we're already
27152713
// force-closing. The monitor update on the required in-memory copy should broadcast
27162714
// the latest local state, which is the best we can do anyway. Thus, it is safe to
27172715
// ignore the result here.
27182716
let _ = self.chain_monitor.update_channel(funding_txo, &monitor_update);
27192717
}
27202718
let mut shutdown_results = Vec::new();
2721-
if let Some(txid) = unbroadcasted_batch_funding_txid {
2719+
if let Some(txid) = shutdown_res.unbroadcasted_batch_funding_txid {
27222720
let mut funding_batch_states = self.funding_batch_states.lock().unwrap();
27232721
let affected_channels = funding_batch_states.remove(&txid).into_iter().flatten();
27242722
let per_peer_state = self.per_peer_state.read().unwrap();
@@ -6245,22 +6243,19 @@ where
62456243
}
62466244

62476245
fn internal_closing_signed(&self, counterparty_node_id: &PublicKey, msg: &msgs::ClosingSigned) -> Result<(), MsgHandleErrInternal> {
6248-
let mut shutdown_result = None;
6249-
let unbroadcasted_batch_funding_txid;
62506246
let per_peer_state = self.per_peer_state.read().unwrap();
62516247
let peer_state_mutex = per_peer_state.get(counterparty_node_id)
62526248
.ok_or_else(|| {
62536249
debug_assert!(false);
62546250
MsgHandleErrInternal::send_err_msg_no_close(format!("Can't find a peer matching the passed counterparty node_id {}", counterparty_node_id), msg.channel_id)
62556251
})?;
6256-
let (tx, chan_option) = {
6252+
let (tx, chan_option, shutdown_result) = {
62576253
let mut peer_state_lock = peer_state_mutex.lock().unwrap();
62586254
let peer_state = &mut *peer_state_lock;
62596255
match peer_state.channel_by_id.entry(msg.channel_id.clone()) {
62606256
hash_map::Entry::Occupied(mut chan_phase_entry) => {
62616257
if let ChannelPhase::Funded(chan) = chan_phase_entry.get_mut() {
6262-
unbroadcasted_batch_funding_txid = chan.context.unbroadcasted_batch_funding_txid();
6263-
let (closing_signed, tx) = try_chan_phase_entry!(self, chan.closing_signed(&self.fee_estimator, &msg), chan_phase_entry);
6258+
let (closing_signed, tx, shutdown_result) = try_chan_phase_entry!(self, chan.closing_signed(&self.fee_estimator, &msg), chan_phase_entry);
62646259
if let Some(msg) = closing_signed {
62656260
peer_state.pending_msg_events.push(events::MessageSendEvent::SendClosingSigned {
62666261
node_id: counterparty_node_id.clone(),
@@ -6273,8 +6268,8 @@ where
62736268
// also implies there are no pending HTLCs left on the channel, so we can
62746269
// fully delete it from tracking (the channel monitor is still around to
62756270
// watch for old state broadcasts)!
6276-
(tx, Some(remove_channel_phase!(self, chan_phase_entry)))
6277-
} else { (tx, None) }
6271+
(tx, Some(remove_channel_phase!(self, chan_phase_entry)), shutdown_result)
6272+
} else { (tx, None, shutdown_result) }
62786273
} else {
62796274
return try_chan_phase_entry!(self, Err(ChannelError::Close(
62806275
"Got a closing_signed message for an unfunded channel!".into())), chan_phase_entry);
@@ -6296,7 +6291,6 @@ where
62966291
});
62976292
}
62986293
self.issue_channel_close_events(&chan.context, ClosureReason::CooperativeClosure);
6299-
shutdown_result = Some((None, Vec::new(), unbroadcasted_batch_funding_txid));
63006294
}
63016295
mem::drop(per_peer_state);
63026296
if let Some(shutdown_result) = shutdown_result {
@@ -6991,15 +6985,17 @@ where
69916985
peer_state.channel_by_id.retain(|channel_id, phase| {
69926986
match phase {
69936987
ChannelPhase::Funded(chan) => {
6994-
let unbroadcasted_batch_funding_txid = chan.context.unbroadcasted_batch_funding_txid();
69956988
match chan.maybe_propose_closing_signed(&self.fee_estimator, &self.logger) {
6996-
Ok((msg_opt, tx_opt)) => {
6989+
Ok((msg_opt, tx_opt, shutdown_result_opt)) => {
69976990
if let Some(msg) = msg_opt {
69986991
has_update = true;
69996992
pending_msg_events.push(events::MessageSendEvent::SendClosingSigned {
70006993
node_id: chan.context.get_counterparty_node_id(), msg,
70016994
});
70026995
}
6996+
if let Some(shutdown_result) = shutdown_result_opt {
6997+
shutdown_results.push(shutdown_result);
6998+
}
70036999
if let Some(tx) = tx_opt {
70047000
// We're done with this channel. We got a closing_signed and sent back
70057001
// a closing_signed with a closing transaction to broadcast.
@@ -7014,7 +7010,6 @@ where
70147010
log_info!(self.logger, "Broadcasting {}", log_tx!(tx));
70157011
self.tx_broadcaster.broadcast_transactions(&[&tx]);
70167012
update_maps_on_chan_removal!(self, &chan.context);
7017-
shutdown_results.push((None, Vec::new(), unbroadcasted_batch_funding_txid));
70187013
false
70197014
} else { true }
70207015
},
@@ -7055,7 +7050,7 @@ where
70557050
// Channel::force_shutdown tries to make us do) as we may still be in initialization,
70567051
// so we track the update internally and handle it when the user next calls
70577052
// timer_tick_occurred, guaranteeing we're running normally.
7058-
if let Some((counterparty_node_id, funding_txo, update)) = failure.0.take() {
7053+
if let Some((counterparty_node_id, funding_txo, update)) = failure.monitor_update.take() {
70597054
assert_eq!(update.updates.len(), 1);
70607055
if let ChannelMonitorUpdateStep::ChannelForceClosed { should_broadcast } = update.updates[0] {
70617056
assert!(should_broadcast);
@@ -9272,16 +9267,16 @@ where
92729267
log_error!(args.logger, " The ChannelMonitor for channel {} is at counterparty commitment transaction number {} but the ChannelManager is at counterparty commitment transaction number {}.",
92739268
&channel.context.channel_id(), monitor.get_cur_counterparty_commitment_number(), channel.get_cur_counterparty_commitment_transaction_number());
92749269
}
9275-
let (monitor_update, mut new_failed_htlcs, batch_funding_txid) = channel.context.force_shutdown(true);
9276-
if batch_funding_txid.is_some() {
9270+
let mut shutdown_result = channel.context.force_shutdown(true);
9271+
if shutdown_result.unbroadcasted_batch_funding_txid.is_some() {
92779272
return Err(DecodeError::InvalidValue);
92789273
}
9279-
if let Some((counterparty_node_id, funding_txo, update)) = monitor_update {
9274+
if let Some((counterparty_node_id, funding_txo, update)) = shutdown_result.monitor_update {
92809275
close_background_events.push(BackgroundEvent::MonitorUpdateRegeneratedOnStartup {
92819276
counterparty_node_id, funding_txo, update
92829277
});
92839278
}
9284-
failed_htlcs.append(&mut new_failed_htlcs);
9279+
failed_htlcs.append(&mut shutdown_result.dropped_outbound_htlcs);
92859280
channel_closures.push_back((events::Event::ChannelClosed {
92869281
channel_id: channel.context.channel_id(),
92879282
user_channel_id: channel.context.get_user_id(),

0 commit comments

Comments
 (0)