-
Notifications
You must be signed in to change notification settings - Fork 378
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
Support persisting ChannelMonitor
s after splicing
#3569
base: main
Are you sure you want to change the base?
Support persisting ChannelMonitor
s after splicing
#3569
Conversation
001463b
to
fb7dcde
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did a pass out of interest, LGTM.
CI is failing as lightning-persister
tests need some adjustments though.
lightning/src/util/persist.rs
Outdated
fn archive_persisted_channel(&self, monitor: &ChannelMonitor<ChannelSigner>) { | ||
let monitor_name = monitor.persistence_key(); | ||
let monitor = match self.read( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wondering if we should take the MonitorName
instead here given Monitor
is re-read below? Reading prevents us from writing a different monitor. Could that ever be the case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By the time we archive, the monitor taken and the one being read should be the same since there can't be any updates. I'd favor taking the MonitorName
instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated accordingly.
lightning/src/util/persist.rs
Outdated
fn archive_persisted_channel(&self, monitor: &ChannelMonitor<ChannelSigner>) { | ||
let monitor_name = monitor.persistence_key(); | ||
let monitor_key = monitor_name.as_str().to_string(); | ||
let monitor = match self.read_channel_monitor_with_updates(monitor_key) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similarly here.
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #3569 +/- ##
==========================================
+ Coverage 88.49% 88.99% +0.49%
==========================================
Files 149 149
Lines 114709 119147 +4438
Branches 114709 119147 +4438
==========================================
+ Hits 101517 106029 +4512
+ Misses 10691 10656 -35
+ Partials 2501 2462 -39 ☔ View full report in Codecov by Sentry. |
f33c2af
to
331c296
Compare
@@ -1398,6 +1400,8 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> { | |||
let mut outputs_to_watch = new_hash_map(); | |||
outputs_to_watch.insert(funding_info.0.txid, vec![(funding_info.0.index as u32, funding_info.1.clone())]); | |||
|
|||
let original_funding_txo = funding_info.0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For v2 channels, since monitors are created before transaction confirmation, this may not point to the one that actually confirmed if it changes due to an RBF. We should probably clarify what we're tracking here. Ultimately, I do think it should track the first confirmed outpoint of the channel.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm... right, this will affect splicing a v1 channel then. IIUC, a v1 channel can be spliced but it will keep its channel id, which was derived from the original funding outpoint. Thus, we need the original_funding_txo
to determine if the ChannelMonitor
is for a v1 channel and continue to use it for the persistence key.
Seems we need to pass in a separate first_confirmed_funding_txo
to ChannelMonitor::new
as an Option<OutPoint>
. For v1 or v2 channel establishment, we would pass None
and set on ChannelMonitor
as first_confirmed_funding_txo.unwrap_or(funding_info.0)
. An RBF attempt on v2 channel establishment would need to reset first_confirmed_funding_txo
. And a splice attempt would need to pass in the appropriate Some(first_confirmed_funding_txo)
.
Does that check out? Presumably, the "scoped" work on ChannelMonitor
for outstanding splices/RBFs would affect the exact mechanism for this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can keep things as is for v1 channels since we can't RBF them, so original_funding_txo
will always be the intended value. For v2 channels is where things change, and I think we could just mutate the existing original_funding_txo
to point to the newly confirmed one if a funding RBF does happen. We just need to rename the field to first_confirmed_funding_txo
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, make sense. Presumably the code will be refactored a bit when adding RBF support to allow updating or not updating depending on whether the RBF is for channel establishment (i.e., for v2 only) or splicing (i.e. for either v1 or v2).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Renamed.
lightning/src/util/persist.rs
Outdated
fn archive_persisted_channel(&self, monitor: &ChannelMonitor<ChannelSigner>) { | ||
let monitor_name = monitor.persistence_key(); | ||
let monitor = match self.read( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By the time we archive, the monitor taken and the one being read should be the same since there can't be any updates. I'd favor taking the MonitorName
instead.
331c296
to
8edede0
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM after squash. I'm not sure how we want to track the changes needed (as a TODO or issue?) for first_confirmed_funding_txo
once we implement RBF support, they could easily be forgotten.
30ae6bd
to
260341b
Compare
Add a TODO at the |
Instead repeating the MonitorName formatting when persisting monitors, DRY up the code to use MonitorName::from instead.
ChannelMonitors are persisted using the corresponding channel's funding outpoint for the key. This is fine for v1 channels since the funding output will never change. However, this is not the case for v2 channels since a splice will result in a new funding outpoint. Support parsing a MonitorName for v2 channels as a ChannelId, which still looks like a outpoint only without an index. This allows differentiating monitors for v1 channels from those of v2 channels.
260341b
to
9e7be36
Compare
Rebased on latest changes to #3554. |
lightning/src/util/persist.rs
Outdated
/// | ||
/// This is useful when you have a `MonitorName` (perhaps retrieved from storage) | ||
/// and need to reconstruct the original data it represents. | ||
fn try_from(name: &str) -> Result<Self, io::Error> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you explain a bit more why we want to add this - what is the anticipated use-case? That someone would do, like, list_keys
then iterate the keys, calling this, and compare against some desired monitor (instead of calculating the two possible keys and doing a lookup by the known key)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not familiar with the exact use case, but this was exposed by #3376. See MonitorName
docs for some examples. FWIW, the latest push removes this now that MonitorName
and MonitorNameSource
are merged.
lightning/src/util/persist.rs
Outdated
/// // Using MonitorName to generate a storage key | ||
/// let storage_key = format!("channel_monitors/{}", monitor_name.as_str()); | ||
/// ``` | ||
#[derive(Debug)] | ||
#[derive(Clone, Debug, Eq, Hash, PartialEq)] | ||
pub struct MonitorName(String); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be nice to make MonitorName
's internals just be MonitorNameSource
, allocating the string when we actually go to use it (and ideally with the ability to write to a io::Write
so that we can append it to the path in FilesystemPersister
without allocating the string up front, since this is a very nontrivial portion of the total allocations I see on my node).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, consolidated it all as a MonitorName
enum. Though FilesystemStore
implements KVStore
, which Persist
has a blanket implementation for, so the conversion is done there (i.e., KVStore::write
takes each path component as a &str
). Therefore, I don't think io::Write
is relevant here.
lightning/src/chain/chainmonitor.rs
Outdated
@@ -118,7 +118,7 @@ pub trait Persist<ChannelSigner: EcdsaChannelSigner> { | |||
/// | |||
/// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager | |||
/// [`Writeable::write`]: crate::util::ser::Writeable::write | |||
fn persist_new_channel(&self, channel_funding_outpoint: OutPoint, monitor: &ChannelMonitor<ChannelSigner>) -> ChannelMonitorUpdateStatus; | |||
fn persist_new_channel(&self, monitor: &ChannelMonitor<ChannelSigner>) -> ChannelMonitorUpdateStatus; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, its a bit awkward to remove the key here but keep it in archive_persisted_channel
. I guess maybe I'd prefer we keep the key since I hadn't considered archive_persisted_channel
's semantics, sorry. Not really a huge deal either way, tho, if you feel strongly otherwise.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense. Added the parameter back to the other methods.
Currently, when a ChannelMonitor is persisted, it's funding txo is used as the key. However, when a channel is spliced, there is a new funding txo. Store the first confirmed funding txo in ChannelMonitor such that the persistence does not change. This will be used for v1 channels for backward compatibility while v2 channels will use the channel ID, which for them is not derived from the funding txo. This prevents needing to migrate previously-persisted ChannelMonitors.
The WatchtowerPersister test utility keys its maps using the channel's funding_outpoint. Since this is being removed from the Persist trait in the next commit, update the maps to be keyed by ChannelId instead. This is fine for testing where there isn't any previously persisted data.
ChannelMonitors are persisted using the corresponding channel's funding outpoint for the key. This is fine for v1 channels since the funding output will never change. However, this is not the case for v2 channels since a splice will result in a new funding outpoint. Instead, use the channel id as the persistence key for v2 channels since this is fixed. For v1 channels, continue to use the funding outpoint for backwards compatibility. Any v1 channel upgraded to a v2 channel via splicing will continue to use the original funding outpoint as the persistence key.
9e7be36
to
6267610
Compare
ChannelMonitor
s are persisted using the corresponding channel's funding outpoint for the key. This is fine for v1 channels since the funding output will never change. However, this is not the case for v2 channels since a splice will result in a new funding outpoint.Instead, use the channel id as the persistence key for v2 channels since this is fixed. For v1 channels, continue to use the funding outpoint for backwards compatibility. Any v1 channel upgraded to a v2 channel via splicing will continue to use the original funding outpoint as the persistence key.
The funding outpoint is thus removed from the
Persist
methods in favor of callingChannelMonitor::persistence_key
.Based on #3554.
Fixes #3325.