Skip to content

Commit dd334b9

Browse files
committed
Hold in-flight monitor updates until background event processing
We previously assumed background events would eventually be processed prior to another `ChannelManager` write, so we would immediately remove all in-flight monitor updates that completed since the last `ChannelManager` serialization. This isn't always the case, so we now keep them all around until we're ready to handle them, i.e., when `process_background_events` is called. This was discovered while fuzzing `chanmon_consistency_target` on the main branch with some changes that allow it to connect blocks. It was triggered by reloading the `ChannelManager` after a monitor update completion for an outgoing HTLC, calling `ChannelManager::best_block_updated`, and reloading the `ChannelManager` once again. A test is included that provides a minimal reproduction of this case.
1 parent 817ab5e commit dd334b9

2 files changed

Lines changed: 162 additions & 22 deletions

File tree

lightning/src/ln/channelmanager.rs

Lines changed: 74 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1277,7 +1277,11 @@ enum BackgroundEvent {
12771277
/// Some [`ChannelMonitorUpdate`] (s) completed before we were serialized but we still have
12781278
/// them marked pending, thus we need to run any [`MonitorUpdateCompletionAction`] (s) pending
12791279
/// on a channel.
1280-
MonitorUpdatesComplete { counterparty_node_id: PublicKey, channel_id: ChannelId },
1280+
MonitorUpdatesComplete {
1281+
counterparty_node_id: PublicKey,
1282+
channel_id: ChannelId,
1283+
highest_update_id_completed: u64,
1284+
},
12811285
}
12821286

12831287
/// A pointer to a channel that is unblocked when an event is surfaced
@@ -8153,7 +8157,36 @@ impl<
81538157
BackgroundEvent::MonitorUpdateRegeneratedOnStartup { counterparty_node_id, funding_txo, channel_id, update } => {
81548158
self.apply_post_close_monitor_update(counterparty_node_id, channel_id, funding_txo, update);
81558159
},
8156-
BackgroundEvent::MonitorUpdatesComplete { counterparty_node_id, channel_id } => {
8160+
BackgroundEvent::MonitorUpdatesComplete {
8161+
counterparty_node_id,
8162+
channel_id,
8163+
highest_update_id_completed,
8164+
} => {
8165+
// Now that we can finally handle the background event, remove all in-flight
8166+
// monitor updates for this channel that we've known to complete, as they have
8167+
// already been persisted to the monitor and can be applied to our internal
8168+
// state such that the channel resumes operation if no new updates have been
8169+
// made since.
8170+
{
8171+
let per_peer_state = self.per_peer_state.read().unwrap();
8172+
let mut peer_state_lock;
8173+
let peer_state_mutex_opt = per_peer_state.get(&counterparty_node_id);
8174+
if peer_state_mutex_opt.is_none() {
8175+
continue;
8176+
}
8177+
peer_state_lock = peer_state_mutex_opt.unwrap().lock().unwrap();
8178+
let peer_state = &mut *peer_state_lock;
8179+
if let btree_map::Entry::Occupied(mut e) =
8180+
peer_state.in_flight_monitor_updates.entry(channel_id)
8181+
{
8182+
let (_, in_flight_updates) = e.get_mut();
8183+
in_flight_updates
8184+
.retain(|update| update.update_id > highest_update_id_completed);
8185+
if in_flight_updates.is_empty() {
8186+
e.remove();
8187+
}
8188+
}
8189+
}
81578190
self.channel_monitor_updated(&channel_id, None, &counterparty_node_id);
81588191
},
81598192
}
@@ -18134,39 +18167,58 @@ impl<
1813418167
($counterparty_node_id: expr, $chan_in_flight_upds: expr, $monitor: expr,
1813518168
$peer_state: expr, $logger: expr, $channel_info_log: expr
1813618169
) => { {
18170+
// When all in-flight updates have completed after we were last serialized, we
18171+
// need to remove them. However, we can't guarantee that the next serialization
18172+
// will have happened after processing the
18173+
// `BackgroundEvent::MonitorUpdatesComplete`, so removing them now could lead to the
18174+
// channel never being resumed as the event would not be regenerated after another
18175+
// reload. At the same time, we don't want to resume the channel now because there
18176+
// may be post-update actions to handle. Therefore, we're forced to keep tracking
18177+
// the completed in-flight updates (but only when they have all completed) until we
18178+
// are processing the `BackgroundEvent::MonitorUpdatesComplete`.
1813718179
let mut max_in_flight_update_id = 0;
18138-
let starting_len = $chan_in_flight_upds.len();
18139-
$chan_in_flight_upds.retain(|upd| upd.update_id > $monitor.get_latest_update_id());
18140-
if $chan_in_flight_upds.len() < starting_len {
18180+
let num_updates_completed = $chan_in_flight_upds
18181+
.iter()
18182+
.filter(|update| {
18183+
max_in_flight_update_id = cmp::max(max_in_flight_update_id, update.update_id);
18184+
update.update_id <= $monitor.get_latest_update_id()
18185+
})
18186+
.count();
18187+
if num_updates_completed > 0 {
1814118188
log_debug!(
1814218189
$logger,
1814318190
"{} ChannelMonitorUpdates completed after ChannelManager was last serialized",
18144-
starting_len - $chan_in_flight_upds.len()
18191+
num_updates_completed,
1814518192
);
1814618193
}
18194+
let all_updates_completed = num_updates_completed == $chan_in_flight_upds.len();
18195+
1814718196
let funding_txo = $monitor.get_funding_txo();
18148-
for update in $chan_in_flight_upds.iter() {
18149-
log_debug!($logger, "Replaying ChannelMonitorUpdate {} for {}channel {}",
18150-
update.update_id, $channel_info_log, &$monitor.channel_id());
18151-
max_in_flight_update_id = cmp::max(max_in_flight_update_id, update.update_id);
18152-
pending_background_events.push(
18153-
BackgroundEvent::MonitorUpdateRegeneratedOnStartup {
18154-
counterparty_node_id: $counterparty_node_id,
18155-
funding_txo: funding_txo,
18156-
channel_id: $monitor.channel_id(),
18157-
update: update.clone(),
18158-
});
18159-
}
18160-
if $chan_in_flight_upds.is_empty() {
18161-
// We had some updates to apply, but it turns out they had completed before we
18162-
// were serialized, we just weren't notified of that. Thus, we may have to run
18163-
// the completion actions for any monitor updates, but otherwise are done.
18197+
if all_updates_completed {
18198+
log_debug!($logger, "All monitor updates completed since the ChannelManager was last serialized");
1816418199
pending_background_events.push(
1816518200
BackgroundEvent::MonitorUpdatesComplete {
1816618201
counterparty_node_id: $counterparty_node_id,
1816718202
channel_id: $monitor.channel_id(),
18203+
highest_update_id_completed: max_in_flight_update_id,
1816818204
});
1816918205
} else {
18206+
$chan_in_flight_upds.retain(|update| {
18207+
let replay = update.update_id > $monitor.get_latest_update_id();
18208+
if replay {
18209+
log_debug!($logger, "Replaying ChannelMonitorUpdate {} for {}channel {}",
18210+
update.update_id, $channel_info_log, &$monitor.channel_id());
18211+
pending_background_events.push(
18212+
BackgroundEvent::MonitorUpdateRegeneratedOnStartup {
18213+
counterparty_node_id: $counterparty_node_id,
18214+
funding_txo: funding_txo,
18215+
channel_id: $monitor.channel_id(),
18216+
update: update.clone(),
18217+
}
18218+
);
18219+
}
18220+
replay
18221+
});
1817018222
$peer_state.closed_channel_monitor_update_ids.entry($monitor.channel_id())
1817118223
.and_modify(|v| *v = cmp::max(max_in_flight_update_id, *v))
1817218224
.or_insert(max_in_flight_update_id);

lightning/src/ln/reload_tests.rs

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@ use crate::prelude::*;
3737

3838
use crate::ln::functional_test_utils::*;
3939

40+
fn get_latest_mon_update_id<'a, 'b, 'c>(
41+
node: &Node<'a, 'b, 'c>, channel_id: ChannelId,
42+
) -> (u64, u64) {
43+
let monitor_id_state = node.chain_monitor.latest_monitor_update_id.lock().unwrap();
44+
monitor_id_state.get(&channel_id).unwrap().clone()
45+
}
46+
4047
#[test]
4148
fn test_funding_peer_disconnect() {
4249
// Test that we can lock in our funding tx while disconnected
@@ -1566,3 +1573,84 @@ fn test_peer_storage() {
15661573
assert!(res.is_err());
15671574
}
15681575

1576+
#[test]
1577+
fn test_hold_completed_inflight_monitor_updates_upon_manager_reload() {
1578+
// Test that if a `ChannelMonitorUpdate` completes after the `ChannelManager` is serialized,
1579+
// but before it is deserialized, we hold any completed in-flight updates until background event
1580+
// processing. Previously, we would remove completed monitor updates from
1581+
// `in_flight_monitor_updates` during deserialization, relying on
1582+
// [`ChannelManager::process_background_events`] to eventually be called before the
1583+
// `ChannelManager` is serialized again such that the channel is resumed and further updates can
1584+
// be made.
1585+
let chanmon_cfgs = create_chanmon_cfgs(2);
1586+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
1587+
let (persister_a, persister_b);
1588+
let (chain_monitor_a, chain_monitor_b);
1589+
1590+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
1591+
let nodes_0_deserialized_a;
1592+
let nodes_0_deserialized_b;
1593+
1594+
let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
1595+
let chan_id = create_announced_chan_between_nodes(&nodes, 0, 1).2;
1596+
1597+
send_payment(&nodes[0], &[&nodes[1]], 1_000_000);
1598+
1599+
chanmon_cfgs[0].persister.set_update_ret(ChannelMonitorUpdateStatus::InProgress);
1600+
1601+
// Send a payment that will be pending due to an async monitor update.
1602+
let (route, payment_hash, _, payment_secret) =
1603+
get_route_and_payment_hash!(nodes[0], nodes[1], 1_000_000);
1604+
let payment_id = PaymentId(payment_hash.0);
1605+
let onion = RecipientOnionFields::secret_only(payment_secret);
1606+
nodes[0].node.send_payment_with_route(route, payment_hash, onion, payment_id).unwrap();
1607+
check_added_monitors(&nodes[0], 1);
1608+
1609+
assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty());
1610+
assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());
1611+
1612+
// Serialize the ChannelManager while the monitor update is still in-flight.
1613+
let node_0_serialized = nodes[0].node.encode();
1614+
1615+
// Now complete the monitor update by calling force_channel_monitor_updated.
1616+
// This updates the monitor's state, but the ChannelManager still thinks it's pending.
1617+
let (_, latest_update_id) = get_latest_mon_update_id(&nodes[0], chan_id);
1618+
nodes[0].chain_monitor.chain_monitor.force_channel_monitor_updated(chan_id, latest_update_id);
1619+
let monitor_serialized_updated = get_monitor!(nodes[0], chan_id).encode();
1620+
1621+
// Reload the node with the updated monitor. Upon deserialization, the ChannelManager will
1622+
// detect that the monitor update completed (monitor's update_id >= the in-flight update_id)
1623+
// and queue a `BackgroundEvent::MonitorUpdatesComplete`.
1624+
nodes[0].node.peer_disconnected(nodes[1].node.get_our_node_id());
1625+
nodes[1].node.peer_disconnected(nodes[0].node.get_our_node_id());
1626+
reload_node!(
1627+
nodes[0],
1628+
test_default_channel_config(),
1629+
&node_0_serialized,
1630+
&[&monitor_serialized_updated[..]],
1631+
persister_a,
1632+
chain_monitor_a,
1633+
nodes_0_deserialized_a
1634+
);
1635+
1636+
// If we serialize again, even though we haven't processed any background events yet, we should
1637+
// still see the `BackgroundEvent::MonitorUpdatesComplete` be regenerated on startup.
1638+
let node_0_serialized = nodes[0].node.encode();
1639+
reload_node!(
1640+
nodes[0],
1641+
test_default_channel_config(),
1642+
&node_0_serialized,
1643+
&[&monitor_serialized_updated[..]],
1644+
persister_b,
1645+
chain_monitor_b,
1646+
nodes_0_deserialized_b
1647+
);
1648+
1649+
// Reconnect the nodes. We should finally see the `update_add_htlc` go out, as the reconnection
1650+
// should first process `BackgroundEvent::MonitorUpdatesComplete, allowing the channel to be
1651+
// resumed.
1652+
let mut reconnect_args = ReconnectArgs::new(&nodes[0], &nodes[1]);
1653+
reconnect_args.pending_htlc_adds = (0, 1);
1654+
reconnect_nodes(reconnect_args);
1655+
}
1656+

0 commit comments

Comments
 (0)