Skip to content

Commit 7746150

Browse files
committed
Delay RAA-after-next processing until PaymentSent is are handled
In 0ad1f4c we fixed a nasty bug where a failure to persist a `ChannelManager` faster than a `ChannelMonitor` could result in the loss of a `PaymentSent` event, eventually resulting in a `PaymentFailed` instead! As noted in that commit, there's still some risk, though its been substantially reduced - if we receive an `update_fulfill_htlc` message for an outbound payment, and persist the initial removal `ChannelMonitorUpdate`, then respond with our own `commitment_signed` + `revoke_and_ack`, followed by receiving our peer's final `revoke_and_ack`, and then persist the `ChannelMonitorUpdate` generated from that, all prior to completing a `ChannelManager` persistence, we'll still forget the HTLC and eventually trigger a `PaymentFailed` rather than the correct `PaymentSent`. Here we fully fix the issue by delaying the final `ChannelMonitorUpdate` persistence until the `PaymentSent` event has been processed and document the fact that a spurious `PaymentFailed` event can still be generated for a sent payment. The original fix in 0ad1f4c is still incredibly useful here, allowing us to avoid blocking the first `ChannelMonitorUpdate` until the event processing completes, as this would cause us to add event-processing delay in our general commitment update latency. Instead, we ultimately race the user handling the `PaymentSent` event with how long it takes our `revoke_and_ack` + `commitment_signed` to make it to our counterparty and receive the response `revoke_and_ack`. This should give the user plenty of time to handle the event before we need to make progress. Sadly, because we change our `ChannelMonitorUpdate` semantics, this change requires a number of test changes, avoiding checking for a post-RAA `ChannelMonitorUpdate` until after we process a `PaymentSent` event. Note that this does not apply to payments we learned the preimage for on-chain - ensuring `PaymentSent` events from such resolutions will be addressed in a future PR. Thus, tests which resolve payments on-chain switch to a direct call to the `expect_payment_sent` function with the claim-expected flag unset.
1 parent a00ab54 commit 7746150

14 files changed

+326
-102
lines changed

lightning-invoice/src/utils.rs

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1364,22 +1364,7 @@ mod test {
13641364
let payment_preimage_opt = if user_generated_pmt_hash { None } else { Some(payment_preimage) };
13651365
expect_payment_claimable!(&nodes[fwd_idx], payment_hash, payment_secret, payment_amt, payment_preimage_opt, invoice.recover_payee_pub_key());
13661366
do_claim_payment_along_route(&nodes[0], &[&vec!(&nodes[fwd_idx])[..]], false, payment_preimage);
1367-
let events = nodes[0].node.get_and_clear_pending_events();
1368-
assert_eq!(events.len(), 2);
1369-
match events[0] {
1370-
Event::PaymentSent { payment_preimage: ref ev_preimage, payment_hash: ref ev_hash, ref fee_paid_msat, .. } => {
1371-
assert_eq!(payment_preimage, *ev_preimage);
1372-
assert_eq!(payment_hash, *ev_hash);
1373-
assert_eq!(fee_paid_msat, &Some(0));
1374-
},
1375-
_ => panic!("Unexpected event")
1376-
}
1377-
match events[1] {
1378-
Event::PaymentPathSuccessful { payment_hash: hash, .. } => {
1379-
assert_eq!(hash, Some(payment_hash));
1380-
},
1381-
_ => panic!("Unexpected event")
1382-
}
1367+
expect_payment_sent(&nodes[0], payment_preimage, None, true, true);
13831368
}
13841369

13851370
#[test]

lightning/src/chain/chainmonitor.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -826,7 +826,7 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner, C: Deref, T: Deref, F: Deref, L
826826
#[cfg(test)]
827827
mod tests {
828828
use crate::{check_added_monitors, check_closed_broadcast, check_closed_event};
829-
use crate::{expect_payment_sent, expect_payment_claimed, expect_payment_sent_without_paths, expect_payment_path_successful, get_event_msg};
829+
use crate::{expect_payment_claimed, expect_payment_path_successful, get_event_msg};
830830
use crate::{get_htlc_update_msgs, get_local_commitment_txn, get_revoke_commit_msgs, get_route_and_payment_hash, unwrap_send_err};
831831
use crate::chain::{ChannelMonitorUpdateStatus, Confirm, Watch};
832832
use crate::chain::channelmonitor::LATENCY_GRACE_PERIOD_BLOCKS;
@@ -909,7 +909,7 @@ mod tests {
909909

910910
let updates = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
911911
nodes[0].node.handle_update_fulfill_htlc(&nodes[1].node.get_our_node_id(), &updates.update_fulfill_htlcs[0]);
912-
expect_payment_sent_without_paths!(nodes[0], payment_preimage_1);
912+
expect_payment_sent(&nodes[0], payment_preimage_1, None, false, false);
913913
nodes[0].node.handle_commitment_signed(&nodes[1].node.get_our_node_id(), &updates.commitment_signed);
914914
check_added_monitors!(nodes[0], 1);
915915
let (as_first_raa, as_first_update) = get_revoke_commit_msgs!(nodes[0], nodes[1].node.get_our_node_id());
@@ -922,7 +922,7 @@ mod tests {
922922
let bs_first_raa = get_event_msg!(nodes[1], MessageSendEvent::SendRevokeAndACK, nodes[0].node.get_our_node_id());
923923

924924
nodes[0].node.handle_update_fulfill_htlc(&nodes[1].node.get_our_node_id(), &bs_second_updates.update_fulfill_htlcs[0]);
925-
expect_payment_sent_without_paths!(nodes[0], payment_preimage_2);
925+
expect_payment_sent(&nodes[0], payment_preimage_2, None, false, false);
926926
nodes[0].node.handle_commitment_signed(&nodes[1].node.get_our_node_id(), &bs_second_updates.commitment_signed);
927927
check_added_monitors!(nodes[0], 1);
928928
nodes[0].node.handle_revoke_and_ack(&nodes[1].node.get_our_node_id(), &bs_first_raa);
@@ -1005,7 +1005,7 @@ mod tests {
10051005
}
10061006
}
10071007

1008-
expect_payment_sent!(nodes[0], payment_preimage);
1008+
expect_payment_sent(&nodes[0], payment_preimage, None, true, false);
10091009
}
10101010

10111011
#[test]

lightning/src/events/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,11 @@ pub enum Event {
472472
/// payment is no longer retryable, due either to the [`Retry`] provided or
473473
/// [`ChannelManager::abandon_payment`] having been called for the corresponding payment.
474474
///
475+
/// In exceedingly rare cases, it is possible that an [`Event::PaymentFailed`] is generated for
476+
/// a payment after an [`Event::PaymentSent`] event for this same payment has already been
477+
/// received and processed. In this case, the [`Event::PaymentFailed`] event MUST be ignored,
478+
/// and the payment MUST be treated as having succeeded.
479+
///
475480
/// [`Retry`]: crate::ln::channelmanager::Retry
476481
/// [`ChannelManager::abandon_payment`]: crate::ln::channelmanager::ChannelManager::abandon_payment
477482
PaymentFailed {

lightning/src/ln/chanmon_update_fail_tests.rs

Lines changed: 72 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1400,6 +1400,7 @@ fn claim_while_disconnected_monitor_update_fail() {
14001400
MessageSendEvent::UpdateHTLCs { ref node_id, ref updates } => {
14011401
assert_eq!(*node_id, nodes[0].node.get_our_node_id());
14021402
nodes[0].node.handle_update_fulfill_htlc(&nodes[1].node.get_our_node_id(), &updates.update_fulfill_htlcs[0]);
1403+
expect_payment_sent(&nodes[0], payment_preimage_1, None, false, false);
14031404
nodes[0].node.handle_commitment_signed(&nodes[1].node.get_our_node_id(), &updates.commitment_signed);
14041405
check_added_monitors!(nodes[0], 1);
14051406

@@ -1437,7 +1438,7 @@ fn claim_while_disconnected_monitor_update_fail() {
14371438

14381439
nodes[0].node.handle_revoke_and_ack(&nodes[1].node.get_our_node_id(), &bs_raa);
14391440
check_added_monitors!(nodes[0], 1);
1440-
expect_payment_sent!(nodes[0], payment_preimage_1);
1441+
expect_payment_path_successful!(nodes[0]);
14411442

14421443
claim_payment(&nodes[0], &[&nodes[1]], payment_preimage_2);
14431444
}
@@ -2191,7 +2192,7 @@ fn test_fail_htlc_on_broadcast_after_claim() {
21912192
expect_pending_htlcs_forwardable_and_htlc_handling_failed!(nodes[1], vec![HTLCDestination::NextHopChannel { node_id: Some(nodes[2].node.get_our_node_id()), channel_id: chan_id_2 }]);
21922193

21932194
nodes[0].node.handle_update_fulfill_htlc(&nodes[1].node.get_our_node_id(), &bs_updates.update_fulfill_htlcs[0]);
2194-
expect_payment_sent_without_paths!(nodes[0], payment_preimage);
2195+
expect_payment_sent(&nodes[0], payment_preimage, None, false, false);
21952196
commitment_signed_dance!(nodes[0], nodes[1], bs_updates.commitment_signed, true, true);
21962197
expect_payment_path_successful!(nodes[0]);
21972198
}
@@ -2437,7 +2438,7 @@ fn do_channel_holding_cell_serialize(disconnect: bool, reload_a: bool) {
24372438
assert!(updates.update_fee.is_none());
24382439
assert_eq!(updates.update_fulfill_htlcs.len(), 1);
24392440
nodes[1].node.handle_update_fulfill_htlc(&nodes[0].node.get_our_node_id(), &updates.update_fulfill_htlcs[0]);
2440-
expect_payment_sent_without_paths!(nodes[1], payment_preimage_0);
2441+
expect_payment_sent(&nodes[1], payment_preimage_0, None, false, false);
24412442
assert_eq!(updates.update_add_htlcs.len(), 1);
24422443
nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &updates.update_add_htlcs[0]);
24432444
updates.commitment_signed
@@ -2454,7 +2455,7 @@ fn do_channel_holding_cell_serialize(disconnect: bool, reload_a: bool) {
24542455
expect_payment_claimable!(nodes[1], payment_hash_1, payment_secret_1, 100000);
24552456
check_added_monitors!(nodes[1], 1);
24562457

2457-
commitment_signed_dance!(nodes[1], nodes[0], (), false, true, false);
2458+
commitment_signed_dance!(nodes[1], nodes[0], (), false, true, false, false);
24582459

24592460
let events = nodes[1].node.get_and_clear_pending_events();
24602461
assert_eq!(events.len(), 2);
@@ -2555,7 +2556,7 @@ fn do_test_reconnect_dup_htlc_claims(htlc_status: HTLCStatusAtDupClaim, second_f
25552556
bs_updates = Some(get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id()));
25562557
assert_eq!(bs_updates.as_ref().unwrap().update_fulfill_htlcs.len(), 1);
25572558
nodes[0].node.handle_update_fulfill_htlc(&nodes[1].node.get_our_node_id(), &bs_updates.as_ref().unwrap().update_fulfill_htlcs[0]);
2558-
expect_payment_sent_without_paths!(nodes[0], payment_preimage);
2559+
expect_payment_sent(&nodes[0], payment_preimage, None, false, false);
25592560
if htlc_status == HTLCStatusAtDupClaim::Cleared {
25602561
commitment_signed_dance!(nodes[0], nodes[1], &bs_updates.as_ref().unwrap().commitment_signed, false);
25612562
expect_payment_path_successful!(nodes[0]);
@@ -2582,7 +2583,7 @@ fn do_test_reconnect_dup_htlc_claims(htlc_status: HTLCStatusAtDupClaim, second_f
25822583
bs_updates = Some(get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id()));
25832584
assert_eq!(bs_updates.as_ref().unwrap().update_fulfill_htlcs.len(), 1);
25842585
nodes[0].node.handle_update_fulfill_htlc(&nodes[1].node.get_our_node_id(), &bs_updates.as_ref().unwrap().update_fulfill_htlcs[0]);
2585-
expect_payment_sent_without_paths!(nodes[0], payment_preimage);
2586+
expect_payment_sent(&nodes[0], payment_preimage, None, false, false);
25862587
}
25872588
if htlc_status != HTLCStatusAtDupClaim::Cleared {
25882589
commitment_signed_dance!(nodes[0], nodes[1], &bs_updates.as_ref().unwrap().commitment_signed, false);
@@ -2779,7 +2780,7 @@ fn double_temp_error() {
27792780
assert_eq!(node_id, nodes[0].node.get_our_node_id());
27802781
nodes[0].node.handle_update_fulfill_htlc(&nodes[1].node.get_our_node_id(), &update_fulfill_1);
27812782
check_added_monitors!(nodes[0], 0);
2782-
expect_payment_sent_without_paths!(nodes[0], payment_preimage_1);
2783+
expect_payment_sent(&nodes[0], payment_preimage_1, None, false, false);
27832784
nodes[0].node.handle_commitment_signed(&nodes[1].node.get_our_node_id(), &commitment_signed_b1);
27842785
check_added_monitors!(nodes[0], 1);
27852786
nodes[0].node.process_pending_htlc_forwards();
@@ -3004,3 +3005,67 @@ fn test_inbound_reload_without_init_mon() {
30043005
do_test_inbound_reload_without_init_mon(false, true);
30053006
do_test_inbound_reload_without_init_mon(false, false);
30063007
}
3008+
3009+
#[test]
3010+
fn test_blocked_chan_preimage_release() {
3011+
// Test that even if a channel's `ChannelMonitorUpdate` flow is blocked waiting on an event to
3012+
// be handled HTLC preimage `ChannelMonitorUpdate`s will still go out.
3013+
let chanmon_cfgs = create_chanmon_cfgs(3);
3014+
let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
3015+
let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]);
3016+
let mut nodes = create_network(3, &node_cfgs, &node_chanmgrs);
3017+
3018+
create_announced_chan_between_nodes(&nodes, 0, 1).2;
3019+
create_announced_chan_between_nodes(&nodes, 1, 2).2;
3020+
3021+
send_payment(&nodes[0], &[&nodes[1], &nodes[2]], 5_000_000);
3022+
3023+
// Tee up two payments in opposite directions across nodes[1], one it sent to generate a
3024+
// PaymentSent event and one it forwards.
3025+
let (payment_preimage_1, payment_hash_1, _) = route_payment(&nodes[1], &[&nodes[2]], 1_000_000);
3026+
let (payment_preimage_2, payment_hash_2, _) = route_payment(&nodes[2], &[&nodes[1], &nodes[0]], 1_000_000);
3027+
3028+
// Claim the first payment to get a `PaymentSent` event (but don't handle it yet).
3029+
nodes[2].node.claim_funds(payment_preimage_1);
3030+
check_added_monitors(&nodes[2], 1);
3031+
expect_payment_claimed!(nodes[2], payment_hash_1, 1_000_000);
3032+
3033+
let cs_htlc_fulfill_updates = get_htlc_update_msgs!(nodes[2], nodes[1].node.get_our_node_id());
3034+
nodes[1].node.handle_update_fulfill_htlc(&nodes[2].node.get_our_node_id(), &cs_htlc_fulfill_updates.update_fulfill_htlcs[0]);
3035+
commitment_signed_dance!(nodes[1], nodes[2], cs_htlc_fulfill_updates.commitment_signed, false);
3036+
check_added_monitors(&nodes[1], 0);
3037+
3038+
// Now claim the second payment on nodes[0], which will ultimately result in nodes[1] trying to
3039+
// claim an HTLC on its channel with nodes[2], but that channel is blocked on the above
3040+
// `PaymentSent` event.
3041+
nodes[0].node.claim_funds(payment_preimage_2);
3042+
check_added_monitors(&nodes[0], 1);
3043+
expect_payment_claimed!(nodes[0], payment_hash_2, 1_000_000);
3044+
3045+
let as_htlc_fulfill_updates = get_htlc_update_msgs!(nodes[0], nodes[1].node.get_our_node_id());
3046+
nodes[1].node.handle_update_fulfill_htlc(&nodes[0].node.get_our_node_id(), &as_htlc_fulfill_updates.update_fulfill_htlcs[0]);
3047+
check_added_monitors(&nodes[1], 1); // We generate only a preimage monitor update
3048+
assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());
3049+
3050+
// Finish the CS dance between nodes[0] and nodes[1].
3051+
commitment_signed_dance!(nodes[1], nodes[0], as_htlc_fulfill_updates.commitment_signed, false);
3052+
check_added_monitors(&nodes[1], 0);
3053+
3054+
let events = nodes[1].node.get_and_clear_pending_events();
3055+
assert_eq!(events.len(), 3);
3056+
if let Event::PaymentSent { .. } = events[0] {} else { panic!(); }
3057+
if let Event::PaymentPathSuccessful { .. } = events[2] {} else { panic!(); }
3058+
if let Event::PaymentForwarded { .. } = events[1] {} else { panic!(); }
3059+
3060+
// The event processing should release the last RAA update.
3061+
check_added_monitors(&nodes[1], 1);
3062+
3063+
// When we fetch the next update the message getter will generate the next update for nodes[2],
3064+
// generating a further monitor update.
3065+
let bs_htlc_fulfill_updates = get_htlc_update_msgs!(nodes[1], nodes[2].node.get_our_node_id());
3066+
check_added_monitors(&nodes[1], 1);
3067+
3068+
nodes[2].node.handle_update_fulfill_htlc(&nodes[1].node.get_our_node_id(), &bs_htlc_fulfill_updates.update_fulfill_htlcs[0]);
3069+
commitment_signed_dance!(nodes[2], nodes[1], bs_htlc_fulfill_updates.commitment_signed, false);
3070+
expect_payment_sent(&nodes[2], payment_preimage_2, None, true, true);
3071+
}

lightning/src/ln/channel.rs

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3143,7 +3143,8 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
31433143
/// waiting on this revoke_and_ack. The generation of this new commitment_signed may also fail,
31443144
/// generating an appropriate error *after* the channel state has been updated based on the
31453145
/// revoke_and_ack message.
3146-
pub fn revoke_and_ack<L: Deref>(&mut self, msg: &msgs::RevokeAndACK, logger: &L) -> Result<(Vec<(HTLCSource, PaymentHash)>, Option<&ChannelMonitorUpdate>), ChannelError>
3146+
pub fn revoke_and_ack<L: Deref>(&mut self, msg: &msgs::RevokeAndACK, logger: &L, hold_mon_update: bool)
3147+
-> Result<(Vec<(HTLCSource, PaymentHash)>, Option<&ChannelMonitorUpdate>), ChannelError>
31473148
where L::Target: Logger,
31483149
{
31493150
if (self.context.channel_state & (ChannelState::ChannelReady as u32)) != (ChannelState::ChannelReady as u32) {
@@ -3323,6 +3324,10 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
33233324
}
33243325
}
33253326

3327+
let release_monitor = self.context.pending_monitor_updates.iter().all(|upd| !upd.blocked) && !hold_mon_update;
3328+
let release_state_str =
3329+
if hold_mon_update { "Holding" } else if release_monitor { "Releasing" } else { "Blocked" };
3330+
33263331
if (self.context.channel_state & ChannelState::MonitorUpdateInProgress as u32) == ChannelState::MonitorUpdateInProgress as u32 {
33273332
// We can't actually generate a new commitment transaction (incl by freeing holding
33283333
// cells) while we can't update the monitor, so we just return what we have.
@@ -3341,7 +3346,11 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
33413346
self.context.monitor_pending_failures.append(&mut revoked_htlcs);
33423347
self.context.monitor_pending_finalized_fulfills.append(&mut finalized_claimed_htlcs);
33433348
log_debug!(logger, "Received a valid revoke_and_ack for channel {} but awaiting a monitor update resolution to reply.", log_bytes!(self.context.channel_id()));
3344-
return Ok((Vec::new(), self.push_ret_blockable_mon_update(monitor_update)));
3349+
self.context.pending_monitor_updates.push(PendingChannelMonitorUpdate {
3350+
update: monitor_update, blocked: !release_monitor,
3351+
});
3352+
return Ok((Vec::new(),
3353+
if release_monitor { self.context.pending_monitor_updates.last().map(|upd| &upd.update) } else { None }));
33453354
}
33463355

33473356
match self.free_holding_cell_htlcs(logger) {
@@ -3352,8 +3361,15 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
33523361
self.context.latest_monitor_update_id = monitor_update.update_id;
33533362
monitor_update.updates.append(&mut additional_update.updates);
33543363

3364+
log_debug!(logger, "Received a valid revoke_and_ack for channel {} with holding cell HTLCs freed. {} monitor update.",
3365+
log_bytes!(self.context.channel_id()), release_state_str);
3366+
33553367
self.monitor_updating_paused(false, true, false, to_forward_infos, revoked_htlcs, finalized_claimed_htlcs);
3356-
Ok((htlcs_to_fail, self.push_ret_blockable_mon_update(monitor_update)))
3368+
self.context.pending_monitor_updates.push(PendingChannelMonitorUpdate {
3369+
update: monitor_update, blocked: !release_monitor,
3370+
});
3371+
Ok((htlcs_to_fail,
3372+
if release_monitor { self.context.pending_monitor_updates.last().map(|upd| &upd.update) } else { None }))
33573373
},
33583374
(None, htlcs_to_fail) => {
33593375
if require_commitment {
@@ -3364,14 +3380,27 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
33643380
self.context.latest_monitor_update_id = monitor_update.update_id;
33653381
monitor_update.updates.append(&mut additional_update.updates);
33663382

3367-
log_debug!(logger, "Received a valid revoke_and_ack for channel {}. Responding with a commitment update with {} HTLCs failed.",
3368-
log_bytes!(self.context.channel_id()), update_fail_htlcs.len() + update_fail_malformed_htlcs.len());
3383+
log_debug!(logger, "Received a valid revoke_and_ack for channel {}. Responding with a commitment update with {} HTLCs failed. {} monitor update.",
3384+
log_bytes!(self.context.channel_id()),
3385+
update_fail_htlcs.len() + update_fail_malformed_htlcs.len(),
3386+
release_state_str);
3387+
33693388
self.monitor_updating_paused(false, true, false, to_forward_infos, revoked_htlcs, finalized_claimed_htlcs);
3370-
Ok((htlcs_to_fail, self.push_ret_blockable_mon_update(monitor_update)))
3389+
self.context.pending_monitor_updates.push(PendingChannelMonitorUpdate {
3390+
update: monitor_update, blocked: !release_monitor,
3391+
});
3392+
Ok((htlcs_to_fail,
3393+
if release_monitor { self.context.pending_monitor_updates.last().map(|upd| &upd.update) } else { None }))
33713394
} else {
3372-
log_debug!(logger, "Received a valid revoke_and_ack for channel {} with no reply necessary.", log_bytes!(self.context.channel_id()));
3395+
log_debug!(logger, "Received a valid revoke_and_ack for channel {} with no reply necessary. {} monitor update.",
3396+
log_bytes!(self.context.channel_id()), release_state_str);
3397+
33733398
self.monitor_updating_paused(false, false, false, to_forward_infos, revoked_htlcs, finalized_claimed_htlcs);
3374-
Ok((htlcs_to_fail, self.push_ret_blockable_mon_update(monitor_update)))
3399+
self.context.pending_monitor_updates.push(PendingChannelMonitorUpdate {
3400+
update: monitor_update, blocked: !release_monitor,
3401+
});
3402+
Ok((htlcs_to_fail,
3403+
if release_monitor { self.context.pending_monitor_updates.last().map(|upd| &upd.update) } else { None }))
33753404
}
33763405
}
33773406
}

0 commit comments

Comments
 (0)