-
Notifications
You must be signed in to change notification settings - Fork 407
Move to a Monitor-Update return from copying around ChannelMonitors #489
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
Move to a Monitor-Update return from copying around ChannelMonitors #489
Conversation
3fa0e35
to
37beb4c
Compare
4dfe046
to
b70e716
Compare
Resolved fuzz issues and rebased to strip out most other dependencies. |
10e52bd
to
f960afe
Compare
Now based directly on master :) |
f960afe
to
5afb7b5
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.
Okay overall, I really like the direction of changes though need to clarify some changes.
// before we forward it. | ||
// | ||
// We will then use HTLCForwardInfo's PendingHTLCInfo to construct an outbound HTLC, with a | ||
// relevant HTLCSource::PreviousHopData filled in to indicate where it came from (which we can use |
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.
Note: information in both HTLCForwardInfo and PreviousHopData are quite overlapping, can't we use same struct at least for metadata and while pending on ChannelManager just include it in a clean action-to-commit+tracking-data new struct (but overall fine with this commit but feel we should rework documentation of HTLC-tracking in a separate PR)
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.
@@ -1021,7 +1013,7 @@ impl<ChanSigner: ChannelKeys> ChannelMonitor<ChanSigner> { | |||
our_to_self_delay: our_to_self_delay, | |||
their_to_self_delay: None, | |||
|
|||
old_secrets: [([0; 32], 1 << 48); 49], | |||
commitment_secrets: CounterpartyCommitmentSecrets::new(), |
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 like this change, but even go further later we can pour CounterPartyCommmitmentSecrets behind KeyInterface after key derivation is moved in OnChainTxHandler. That way we can remove revocation secret holder from ChannelMonitor. get_min_seen_secret
may be wrap into a is_revoked
method, and either accessing revocation secret for local detection or try to decrypt pre-signed justice tx for watchtowers.
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, I dunno about KeyInterface - its a "user provides us static data" trait, not an "updated dynamically as Channel changes" - right now we only have one thing that the user has to be careful about persisting to disk at the appropriate time, adding more sounds super complicated.
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.
No need to do anything now but would revisit when work on watchtowers. In case of leak of commitment_secret + revocation_basepoint_secret, a misbehaving remote peer would be able to alleviate its punishment so an implementor should be able to get it out-of-memory. Not necessarily behind KeyInterface.
@@ -348,6 +348,7 @@ pub(super) struct Channel<ChanSigner: ChannelKeys> { | |||
their_shutdown_scriptpubkey: Option<Script>, | |||
|
|||
channel_monitor: ChannelMonitor<ChanSigner>, | |||
commitment_secrets: CounterpartyCommitmentSecrets, |
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.
Same here, I think that's temporary before to abstract it behind KeyInterface. Revocation secret by themselves are useless if you can't get access to the local revocation_basepoint_secret but I would rather keep them out of memory.
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.
Same here - this is a constantly-changing struct.
lightning/src/ln/channelmonitor.rs
Outdated
/// avoid this (or call unset_funding_info) on a monitor you wish to send to a watchtower as it | ||
/// provides slightly better privacy. | ||
/// avoid this on a monitor you wish to send to a watchtower as it provides slightly better | ||
/// privacy. |
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.
"(at the price of more computation for watchtower implementation and though privacy enhancement is lost in case of remote broadcast of revoked commitment transaction)"
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.
Ehh, hopefully it doesn't cause more CPU just more blocks to fetch. Note that this is an internal function and, three commits later, this and a bunch of the Option<>-setting set_* fns in ChannelMonitor are removed wholesale.
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.
Hmmm I think it's more CPU because you have to try to decrypt every blob_punishment_txn instead of just looking for a spend of the funding_txo. No need to add comment like you said it's internal function. Watchtowers trade-off should be documented on a higher-level.
Ok((update_fulfill_msg_option, additional_monitor_update_opt)) => { | ||
update_fulfill_htlcs.push(update_fulfill_msg_option.unwrap()); | ||
if let Some(mut additional_monitor_update) = additional_monitor_update_opt { | ||
monitor_update.updates.append(&mut additional_monitor_update.updates); |
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.
Shouldn't you decrement latest_monitor_update_id here too given it possible increase in get_update_fulfill_htlc ? (Or rather drop the decrement for next send_commitment_no_status_check
as updates_id of monitor_update has already been incremented?)
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, AFAICT we should fix it later in the fn in any case. I don't see a control flow path where we don't, or were you just saying for belt-and-suspenders?
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.
Offline discussion turned up the issue here is that the comments all just refer to send_commitment_no_status_check (even though many other fns may increment it during the runtime here) but the code is correct. Comments have been updated.
@@ -2090,7 +2090,16 @@ impl<ChanSigner: ChannelKeys, M: Deref> ChannelManager<ChanSigner, M> where M::T | |||
if chan.get().get_their_node_id() != *their_node_id { | |||
return Err(MsgHandleErrInternal::send_err_msg_no_close("Got a message for a channel from the wrong node!", msg.channel_id)); | |||
} | |||
let monitor_update = try_chan_entry!(self, chan.get_mut().funding_signed(&msg), channel_state, chan); | |||
let monitor_update = match chan.get_mut().funding_signed(&msg) { |
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.
This part shouldn't be in previous commit or is there anything new in this one which prevent its?
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.
The difference is we're not allowed to lose a monitor update anymore - previously if we were waiting on the user to finish updating a monitor we could just return ChannelError::Ignore("Previous monitor update failure ...")))
as we'd provide them a full copy again later, but now we have to make sure any monitor updates generated are returned even with an Err.
lightning/src/ln/channel.rs
Outdated
self.logger.clone())); | ||
|
||
self.channel_monitor.as_mut().unwrap().provide_latest_remote_commitment_tx_info(&remote_initial_commitment_tx, Vec::new(), self.cur_remote_commitment_transaction_number, self.their_cur_commitment_point.unwrap()); | ||
self.channel_monitor.as_mut().unwrap().provide_latest_local_commitment_tx_info(local_initial_commitment_tx, local_keys, self.feerate_per_kw, Vec::new()).unwrap(); |
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't we just store what we need for broadcast of latest local state and drop ChannelMonitor out of Channel ? States will be only provided with new ChannelMonitorUpdates interfaces? (maybe need to cache basic info though). Also getting the minimum operation state from the Channel at anytime would let you spawn new ChannelMonitor at any-time in channel lifecycle (can we do this right now?)
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't we just store what we need for broadcast of latest local state and drop ChannelMonitor out of Channel ?
Yep, thats the next step! I don't have a commit for it yet cause it requires some refactoring of ChannelMonitor and I didn't want to step on your toes that much :p.
States will be only provided with new ChannelMonitorUpdates interfaces? (maybe need to cache basic info though). Also getting the minimum operation state from the Channel at anytime would let you spawn new ChannelMonitor at any-time in channel lifecycle (can we do this right now?)
Not sure what you meant by the first part, but, at least in my head, there would be no way to build a ChannelMonitor from a Channel after initialization - the docs for ManyChannelMonitor and ChannelMonitorUpdateErr always require a local store of ChannelMonitors and I don't know that there is much use in trying to get around that.
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 think we are in-sync on this, let do this after my ChannelMonitor refactor.
@@ -3146,7 +3145,9 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> { | |||
} | |||
if header.bitcoin_hash() != self.last_block_connected { | |||
self.last_block_connected = header.bitcoin_hash(); | |||
self.channel_monitor.last_block_hash = self.last_block_connected; | |||
if let Some(channel_monitor) = self.channel_monitor.as_mut() { | |||
channel_monitor.last_block_hash = self.last_block_connected; |
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 found this a bit confusing given that you already feed ChannelMonitor with block in any ManyChannelMonitor implementation (and our API should allow block skew for watchtowers having a different chain access than client)
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.
This should just be dropped when we remove Channel's local ChannelMonitor copy, its just a historical quirk (and note that the internal copy is never used for anything other than local transaction creation now).
104775f
to
0541124
Compare
This also renames PendingForwardHTLCInfo to PendingHTLCInfo since it now also encompasses Pending *Received* HTLCs.
In order to drop the ChannelMonitor from Channel, we need to track remote per_commitment_secrets outside of the monitor to validate new ones as they come in. This just moves the current code from ChannelMonitor into a new CounterpartyCommitmentSecrets struct in chan_utils.
In the process of removing a local ChannelMonitor in each Channel, we need to track our counterpartys' commitment secrets so that we can check them locally instead of calling our channel monitor to do that work for us.
Currently Channel relies on its own internal channel_monitor copy to keep track of funding_txo information, which is both a bit awkward and not ideal if we want to get rid of the ChannelMonitor copy in Channel. Instead, just duplicate it (its small) and keep it directly in Channel, allowing us to remove the (super awkward) ChannelMonitor::unset_funding_txo().
0541124
to
ddb82e2
Compare
ACK ddb82e2 minus #489 (comment) after offline discussion |
Diff is:
|
ddb82e2
to
c1aebf7
Compare
This is the first step in migrating ChannelMonitor updating logic to use incremental Update objects instead of copying the ChannelMonitors themselves and insert_combine()ing them. This adds most of the scaffolding and updates relevant comments to refer to the new architecture, without changing how any actual updates occur.
This is the first of several steps to update ChannelMonitor updates to use the new ChannelMonitorUpdate objects, demonstrating how the new flow works in Channel.
There is little risk of misusing this as there's not much in the way of other ways you may want to serialize bitcoin::Transaction
This is a rather big step towards using the new ChannelMonitorUpdate flow, using it in the various commitment signing and commitment update message processing functions in Channel. Becase they all often call each other, they all have to be updated as a group, resulting in the somewhat large diff in this commit. In order to keep the update_ids strictly increasing by one for ease of use on the user end, we have to play some games with the latest_monitor_update_id field, though its generally still pretty readable, and the pattern of "get an update_id at the start, and use the one we got at the start when returning, irrespective of what other calls into the Channel during that time did" is relatively straightforward.
This prepares for only creating the ChannelMonitor on funding by removing any channel_monitor calls from Channel open/accept-time to funding-signed time.
This is a rather huge diff, almost entirely due to removing the type parameter from ChannelError which was added in c20e930 due to holding the ChannelKeys in ChannelMonitors.
This removes most of the reliance on ChannelMonitor Clone, creating them in Channel only at the time when we need to start monitoring the chain.
This removes the ability to merge ChannelMonitors in favor of explicit ChannelMonitorUpdates. It further removes ChannelManager::test_restore_channel_monitor in favor of the new ChannelManager::channel_monitor_updated method, which explicitly confirms a set of updates instead of providing the latest copy of each ChannelMonitor to the user. This removes almost all need for Channels to have the latest channel_monitor, except for broadcasting the latest local state.
This removes the somewhat-easy-to-misuse Clone from ChannelMonitors, opening us up to being able to track Events in ChannelMonitors with less risk of misuse. Sadly it doesn't remove the Clone requirement for ChannelKeys, though gets us much closer - we now just need to request a second copy once when we go to create the ChannelMonitors.
c1aebf7
to
08db88c
Compare
Gonna merge to get it off the plate. If you're not happy with any of the docs, zero harm in followup PRs :). |
This tackles #387, migrating us to a much more robust way of handling ChannelMonitors. I still need to rewrite the chanmon_consistency fuzz target based on this, plus maybe rebase it onto master instead of the ever-growing Tower of Babylon. It doesn't quite remove the local ChannelMonitor copy from Channel, which is definitely an important next step, but it gets us most of the way there.