Skip to content

Persist entire monitor if there is an error while applying monitor_update #2621

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Sep 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 38 additions & 20 deletions lightning/src/chain/chainmonitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,23 +47,35 @@ use core::ops::Deref;
use core::sync::atomic::{AtomicUsize, Ordering};
use bitcoin::secp256k1::PublicKey;

#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
/// A specific update's ID stored in a `MonitorUpdateId`, separated out to make the contents
/// entirely opaque.
enum UpdateOrigin {
/// An update that was generated by the `ChannelManager` (via our `chain::Watch`
/// implementation). This corresponds to an actual [`ChannelMonitorUpdate::update_id`] field
/// and [`ChannelMonitor::get_latest_update_id`].
OffChain(u64),
/// An update that was generated during blockchain processing. The ID here is specific to the
/// generating [`ChainMonitor`] and does *not* correspond to any on-disk IDs.
ChainSync(u64),
mod update_origin {
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
/// A specific update's ID stored in a `MonitorUpdateId`, separated out to make the contents
/// entirely opaque.
pub(crate) enum UpdateOrigin {
/// An update that was generated by the `ChannelManager` (via our [`crate::chain::Watch`]
/// implementation). This corresponds to an actual [ChannelMonitorUpdate::update_id] field
/// and [ChannelMonitor::get_latest_update_id].
///
/// [ChannelMonitor::get_latest_update_id]: crate::chain::channelmonitor::ChannelMonitor::get_latest_update_id
/// [ChannelMonitorUpdate::update_id]: crate::chain::channelmonitor::ChannelMonitorUpdate::update_id
OffChain(u64),
/// An update that was generated during blockchain processing. The ID here is specific to the
/// generating [ChannelMonitor] and does *not* correspond to any on-disk IDs.
///
/// [ChannelMonitor]: crate::chain::channelmonitor::ChannelMonitor
ChainSync(u64),
}
}

#[cfg(any(feature = "_test_utils", test))]
pub(crate) use update_origin::UpdateOrigin;
#[cfg(not(any(feature = "_test_utils", test)))]
use update_origin::UpdateOrigin;

/// An opaque identifier describing a specific [`Persist`] method call.
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub struct MonitorUpdateId {
contents: UpdateOrigin,
pub(crate) contents: UpdateOrigin,
}

impl MonitorUpdateId {
Expand Down Expand Up @@ -152,8 +164,8 @@ pub trait Persist<ChannelSigner: WriteableEcdsaChannelSigner> {
/// updated monitor itself to disk/backups. See the [`Persist`] trait documentation for more
/// details.
///
/// During blockchain synchronization operations, this may be called with no
/// [`ChannelMonitorUpdate`], in which case the full [`ChannelMonitor`] needs to be persisted.
/// During blockchain synchronization operations, and in some rare cases, this may be called with
/// no [`ChannelMonitorUpdate`], in which case the full [`ChannelMonitor`] needs to be persisted.
/// Note that after the full [`ChannelMonitor`] is persisted any previous
/// [`ChannelMonitorUpdate`]s which were persisted should be discarded - they can no longer be
/// applied to the persisted [`ChannelMonitor`] as they were already applied.
Expand Down Expand Up @@ -752,14 +764,20 @@ where C::Target: chain::Filter,
let monitor = &monitor_state.monitor;
log_trace!(self.logger, "Updating ChannelMonitor for channel {}", log_funding_info!(monitor));
let update_res = monitor.update_monitor(update, &self.broadcaster, &*self.fee_estimator, &self.logger);
if update_res.is_err() {
log_error!(self.logger, "Failed to update ChannelMonitor for channel {}.", log_funding_info!(monitor));
}
// Even if updating the monitor returns an error, the monitor's state will
// still be changed. So, persist the updated monitor despite the error.

let update_id = MonitorUpdateId::from_monitor_update(update);
let mut pending_monitor_updates = monitor_state.pending_monitor_updates.lock().unwrap();
let persist_res = self.persister.update_persisted_channel(funding_txo, Some(update), monitor, update_id);
let persist_res = if update_res.is_err() {
// Even if updating the monitor returns an error, the monitor's state will
// still be changed. Therefore, we should persist the updated monitor despite the error.
// We don't want to persist a `monitor_update` which results in a failure to apply later
// while reading `channel_monitor` with updates from storage. Instead, we should persist
// the entire `channel_monitor` here.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can make this "Instead, we should persist the entire channel_monitor here, otherwise we will fail to initialize this monitor during restart."

But wasn't sure if it adds more confusion.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The previous sentence makes that clear imo.

log_warn!(self.logger, "Failed to update ChannelMonitor for channel {}. Going ahead and persisting the entire ChannelMonitor", log_funding_info!(monitor));
self.persister.update_persisted_channel(funding_txo, None, monitor, update_id)
} else {
self.persister.update_persisted_channel(funding_txo, Some(update), monitor, update_id)
};
match persist_res {
ChannelMonitorUpdateStatus::InProgress => {
pending_monitor_updates.push(update_id);
Expand Down
5 changes: 3 additions & 2 deletions lightning/src/util/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use crate::chain::chaininterface;
use crate::chain::chaininterface::ConfirmationTarget;
use crate::chain::chaininterface::FEERATE_FLOOR_SATS_PER_KW;
use crate::chain::chainmonitor;
use crate::chain::chainmonitor::MonitorUpdateId;
use crate::chain::chainmonitor::{MonitorUpdateId, UpdateOrigin};
use crate::chain::channelmonitor;
use crate::chain::channelmonitor::MonitorEvent;
use crate::chain::transaction::OutPoint;
Expand Down Expand Up @@ -419,7 +419,8 @@ impl<Signer: sign::WriteableEcdsaChannelSigner> chainmonitor::Persist<Signer> fo
if let Some(update_ret) = self.update_rets.lock().unwrap().pop_front() {
ret = update_ret;
}
if update.is_none() {
let is_chain_sync = if let UpdateOrigin::ChainSync(_) = update_id.contents { true } else { false };
if is_chain_sync {
self.chain_sync_monitor_persistences.lock().unwrap().entry(funding_txo).or_insert(HashSet::new()).insert(update_id);
} else {
self.offchain_monitor_updates.lock().unwrap().entry(funding_txo).or_insert(HashSet::new()).insert(update_id);
Expand Down