Skip to content

Commit cfca2dc

Browse files
committed
Add a method to avoid re-persisting monitors during startup
Prior to LDK 0.1, in rare cases we could replay payment claims to `ChannelMonitor`s on startup, which we then expected to be persisted prior to normal node operation. This required re-persisting `ChannelMonitor`s after deserializing the `ChannelManager`, delaying startup in some cases substantially. In 0.1 we fixed this, moving claim replays to the background to run after the `ChannelManager` starts operating (and only updating/persisting changes to the `ChannelMonitor`s which need it). However, we didn't actually enable this meaningfully in our API - nearly all users use our `ChainMonitor` and the only way to get a chanel into `ChainMonitor` is through the normal flow which expects to persist. Here we add a simple method to load `ChannelMonitor`s into the `ChainMonitor` without persisting them.
1 parent ed1f304 commit cfca2dc

File tree

5 files changed

+97
-2
lines changed

5 files changed

+97
-2
lines changed

lightning/src/chain/chainmonitor.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -825,6 +825,53 @@ where
825825

826826
self.pending_send_only_events.lock().unwrap().push(send_peer_storage_event)
827827
}
828+
829+
/// Loads a [`ChannelMonitor`] which already exists on disk after startup.
830+
///
831+
/// Using this over [`chain::Watch::watch_channel`] avoids re-persisting a [`ChannelMonitor`]
832+
/// that hasn't changed, slowing down startup.
833+
///
834+
/// Note that this method *can* be used if additional blocks were replayed against the
835+
/// [`ChannelMonitor`] or if a [`ChannelMonitorUpdate`] loaded from disk was replayed such that
836+
/// it will replayed on startup, and in general can only *not* be used if you directly accessed
837+
/// the [`ChannelMonitor`] and changed its state in some way that will not be replayed again on
838+
/// a restart. Such direct access should generally never occur for most LDK-based nodes.
839+
///
840+
/// For [`ChannelMonitor`]s which were last serialized by an LDK version prior to 0.1 this will
841+
/// fall back to calling [`chain::Watch::watch_channel`] and persisting the [`ChannelMonitor`].
842+
/// See the release notes for LDK 0.1 for more information on this requirement.
843+
///
844+
/// [`ChannelMonitor`]s which do not need to be persisted (i.e. were last written by LDK 0.1 or
845+
/// later) will be loaded without persistence and this method will return
846+
/// [`ChannelMonitorUpdateStatus::Completed`].
847+
pub fn load_existing_monitor(
848+
&self, channel_id: ChannelId, monitor: ChannelMonitor<ChannelSigner>,
849+
) -> Result<ChannelMonitorUpdateStatus, ()> {
850+
if !monitor.written_by_0_1_or_later() {
851+
return chain::Watch::watch_channel(self, channel_id, monitor);
852+
}
853+
854+
let logger = WithChannelMonitor::from(&self.logger, &monitor, None);
855+
let mut monitors = self.monitors.write().unwrap();
856+
let entry = match monitors.entry(channel_id) {
857+
hash_map::Entry::Occupied(_) => {
858+
log_error!(logger, "Failed to add new channel data: channel monitor for given channel ID is already present");
859+
return Err(());
860+
},
861+
hash_map::Entry::Vacant(e) => e,
862+
};
863+
log_trace!(
864+
logger,
865+
"Loaded existing ChannelMonitor for channel {}",
866+
log_funding_info!(monitor)
867+
);
868+
if let Some(ref chain_source) = self.chain_source {
869+
monitor.load_outputs_to_watch(chain_source, &self.logger);
870+
}
871+
entry.insert(MonitorHolder { monitor, pending_monitor_updates: Mutex::new(Vec::new()) });
872+
873+
Ok(ChannelMonitorUpdateStatus::Completed)
874+
}
828875
}
829876

830877
impl<

lightning/src/chain/channelmonitor.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1300,6 +1300,11 @@ pub(crate) struct ChannelMonitorImpl<Signer: EcdsaChannelSigner> {
13001300
// found at `Self::funding`. We don't use the term "renegotiated", as the currently locked
13011301
// `FundingScope` could be one that was renegotiated.
13021302
alternative_funding_confirmed: Option<(Txid, u32)>,
1303+
1304+
/// [`ChannelMonitor`]s written by LDK prior to 0.1 need to be re-persisted after startup. To
1305+
/// make deciding whether to do so simple, here we track whether this monitor was last written
1306+
/// prior to 0.1.
1307+
written_by_0_1_or_later: bool,
13031308
}
13041309

13051310
// Macro helper to access holder commitment HTLC data (including both non-dust and dust) while
@@ -1803,6 +1808,8 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
18031808
prev_holder_htlc_data: None,
18041809

18051810
alternative_funding_confirmed: None,
1811+
1812+
written_by_0_1_or_later: true,
18061813
})
18071814
}
18081815

@@ -1936,6 +1943,10 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
19361943
self.inner.lock().unwrap().get_funding_txo()
19371944
}
19381945

1946+
pub(crate) fn written_by_0_1_or_later(&self) -> bool {
1947+
self.inner.lock().unwrap().written_by_0_1_or_later
1948+
}
1949+
19391950
/// Gets the funding script of the channel this ChannelMonitor is monitoring for.
19401951
pub fn get_funding_script(&self) -> ScriptBuf {
19411952
self.inner.lock().unwrap().get_funding_script()
@@ -6080,6 +6091,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
60806091
(32, pending_funding, optional_vec),
60816092
(34, alternative_funding_confirmed, option),
60826093
});
6094+
let written_by_0_1_or_later = payment_preimages_with_info.is_some();
60836095
if let Some(payment_preimages_with_info) = payment_preimages_with_info {
60846096
if payment_preimages_with_info.len() != payment_preimages.len() {
60856097
return Err(DecodeError::InvalidValue);
@@ -6250,6 +6262,8 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
62506262
prev_holder_htlc_data,
62516263

62526264
alternative_funding_confirmed,
6265+
6266+
written_by_0_1_or_later,
62536267
})))
62546268
}
62556269
}

lightning/src/ln/channelmanager.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15396,9 +15396,13 @@ impl Readable for VecDeque<(Event, Option<EventCompletionAction>)> {
1539615396
/// This is important if you have replayed a nontrivial number of blocks in step (4), allowing
1539715397
/// you to avoid having to replay the same blocks if you shut down quickly after startup. It is
1539815398
/// otherwise not required.
15399+
///
1539915400
/// Note that if you're using a [`ChainMonitor`] for your [`chain::Watch`] implementation, you
1540015401
/// will likely accomplish this as a side-effect of calling [`chain::Watch::watch_channel`] in
1540115402
/// the next step.
15403+
///
15404+
/// If you wish to avoid this for performance reasons, use
15405+
/// [`ChainMonitor::load_existing_monitor`].
1540215406
/// 7) Move the [`ChannelMonitor`]s into your local [`chain::Watch`]. If you're using a
1540315407
/// [`ChainMonitor`], this is done by calling [`chain::Watch::watch_channel`].
1540415408
///
@@ -15413,6 +15417,7 @@ impl Readable for VecDeque<(Event, Option<EventCompletionAction>)> {
1541315417
/// which you've already broadcasted the transaction.
1541415418
///
1541515419
/// [`ChainMonitor`]: crate::chain::chainmonitor::ChainMonitor
15420+
/// [`ChainMonitor::load_existing_monitor`]: crate::chain::chainmonitor::ChainMonitor::load_existing_monitor
1541615421
pub struct ChannelManagerReadArgs<
1541715422
'a,
1541815423
M: Deref,

lightning/src/ln/functional_test_utils.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1324,8 +1324,8 @@ pub fn _reload_node<'a, 'b, 'c>(
13241324
for monitor in monitors_read.drain(..) {
13251325
let channel_id = monitor.channel_id();
13261326
assert_eq!(
1327-
node.chain_monitor.watch_channel(channel_id, monitor),
1328-
Ok(ChannelMonitorUpdateStatus::Completed)
1327+
node.chain_monitor.load_existing_monitor(channel_id, monitor),
1328+
Ok(ChannelMonitorUpdateStatus::Completed),
13291329
);
13301330
check_added_monitors!(node, 1);
13311331
}

lightning/src/util/test_utils.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,35 @@ impl<'a> TestChainMonitor<'a> {
515515
self.latest_monitor_update_id.lock().unwrap().get(channel_id).unwrap().clone();
516516
self.chain_monitor.channel_monitor_updated(*channel_id, latest_update).unwrap();
517517
}
518+
519+
pub fn load_existing_monitor(
520+
&self, channel_id: ChannelId, monitor: ChannelMonitor<TestChannelSigner>,
521+
) -> Result<chain::ChannelMonitorUpdateStatus, ()> {
522+
#[cfg(feature = "std")]
523+
if let Some(blocker) = &*self.write_blocker.lock().unwrap() {
524+
blocker.recv().unwrap();
525+
}
526+
527+
// At every point where we get a monitor update, we should be able to send a useful monitor
528+
// to a watchtower and disk...
529+
let mut w = TestVecWriter(Vec::new());
530+
monitor.write(&mut w).unwrap();
531+
let new_monitor = <(BlockHash, ChannelMonitor<TestChannelSigner>)>::read(
532+
&mut io::Cursor::new(&w.0),
533+
(self.keys_manager, self.keys_manager),
534+
)
535+
.unwrap()
536+
.1;
537+
// Note that a ChannelMonitor might not round-trip exactly here as we have tests that were
538+
// serialized prior to LDK 0.1 and re-serializing them will flip the "written after LDK
539+
// 0.1" flag.
540+
self.latest_monitor_update_id
541+
.lock()
542+
.unwrap()
543+
.insert(channel_id, (monitor.get_latest_update_id(), monitor.get_latest_update_id()));
544+
self.added_monitors.lock().unwrap().push((channel_id, monitor));
545+
self.chain_monitor.load_existing_monitor(channel_id, new_monitor)
546+
}
518547
}
519548
impl<'a> chain::Watch<TestChannelSigner> for TestChainMonitor<'a> {
520549
fn watch_channel(

0 commit comments

Comments
 (0)