Skip to content

Encapsulate Channel enum variants inside a struct #3550

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

Conversation

jkczyz
Copy link
Contributor

@jkczyz jkczyz commented Jan 17, 2025

Instead of exposing the Channel enum variants, make a Channel struct that wraps a ChannelPhase enum. This allows updating a Channel's phase without consuming it, which isn't possible when it is in a map without removing the entry first. Then move phase transitions can from ChannelManager to Channel. This allows for simpler logic in ChannelManager since the channel does not need to removed and then re-added into the channel_by_id map during a phase transition (or failed one).

@jkczyz jkczyz force-pushed the 2025-01-refactor-channel-phase branch from effabbb to 9773f1a Compare January 17, 2025 23:21
Copy link
Contributor

@optout21 optout21 left a comment

Choose a reason for hiding this comment

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

Looks good (so far)

@jkczyz jkczyz added weekly goal Someone wants to land this this week and removed weekly goal Someone wants to land this this week labels Jan 21, 2025
@jkczyz
Copy link
Contributor Author

jkczyz commented Jan 21, 2025

Ok, AFAICT, the PR as-is should have changed all the possible phase transitions where we were removing and re-inserting into a map (except when going from a temporary channel id). Reviewers, please check that this is the case by looking at the commits introducing From trait implementations in #3513.

FYI, the changes in the last commit may interfere with #3423.

@jkczyz jkczyz marked this pull request as ready for review January 21, 2025 19:49
Comment on lines 8165 to 8189
self.chain_monitor
.watch_channel(funded_chan.context.get_funding_txo().unwrap(), monitor)
.map_err(|()| {
// We weren't able to watch the channel to begin with, so no
// updates should be made on it. Previously, full_stack_target
// found an (unreachable) panic when the monitor update contained
// within `shutdown_finish` was applied.
chan.unset_funding_info(msg.channel_id);
return Err(convert_channel_err!(self, peer_state, e, chan, &msg.channel_id, FUNDED_CHANNEL).1);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was a little confused by this comment given the earlier version removed the entry from the map, and this new version uses try_channel_entry to remove it on error below. i.e., Why do we need to call unset_funding_info if the channel is no longer in the map?

Copy link
Contributor

Choose a reason for hiding this comment

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

We only need to call it in cases where the new channel conflicts with an existing channel. When locked_close_channel eventually gets called on the new channel, it needs to have its funding info removed, otherwise we'll remove the mapping in outpoint_to_peer for the original channel, which is crucial for its handling of MonitorEvents. If we pursue #3554 all the way, that map can finally be removed.

@jkczyz jkczyz added the weekly goal Someone wants to land this this week label Jan 21, 2025
Copy link
Contributor

@wpaulino wpaulino left a comment

Choose a reason for hiding this comment

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

Some other cases where I'd imagine a phase transition happens:

  • Failed splice negotiation on an already funded channel: not yet implemented, OK for now
  • Transaction reorg after confirmation: turns out we just force close anyway, and we don't transition back to unfunded regardless of the changes proposed here

@jkczyz jkczyz force-pushed the 2025-01-refactor-channel-phase branch from 9773f1a to f3cd992 Compare January 22, 2025 17:59
Copy link

codecov bot commented Jan 22, 2025

Codecov Report

Attention: Patch coverage is 71.69043% with 139 lines in your changes missing coverage. Please review.

Project coverage is 89.54%. Comparing base (4579e63) to head (f3cd992).
Report is 83 commits behind head on main.

Files with missing lines Patch % Lines
lightning/src/ln/channel.rs 50.34% 69 Missing and 3 partials ⚠️
lightning/src/ln/channelmanager.rs 79.93% 58 Missing and 7 partials ⚠️
lightning/src/ln/functional_tests.rs 88.23% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3550      +/-   ##
==========================================
+ Coverage   88.57%   89.54%   +0.96%     
==========================================
  Files         149      149              
  Lines      114539   122358    +7819     
  Branches   114539   122358    +7819     
==========================================
+ Hits       101449   109561    +8112     
+ Misses      10602    10370     -232     
+ Partials     2488     2427      -61     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@jkczyz jkczyz force-pushed the 2025-01-refactor-channel-phase branch from f3cd992 to e84d9d1 Compare January 23, 2025 18:31
@jkczyz jkczyz force-pushed the 2025-01-refactor-channel-phase branch from e84d9d1 to f7f5567 Compare January 23, 2025 20:13
Copy link
Contributor

@wpaulino wpaulino left a comment

Choose a reason for hiding this comment

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

LGTM after squash

@jkczyz jkczyz force-pushed the 2025-01-refactor-channel-phase branch from f7f5567 to 4a30d10 Compare January 23, 2025 22:05
@jkczyz
Copy link
Contributor Author

jkczyz commented Jan 23, 2025

LGTM after squash

@optout21 Let me know if you'd like to me to squash the fixups

Copy link
Contributor

@carlaKC carlaKC left a comment

Choose a reason for hiding this comment

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

New to the codebase so comments are all non-blocking, nice refactor!

@jkczyz jkczyz force-pushed the 2025-01-refactor-channel-phase branch from 4a30d10 to 22436bb Compare January 24, 2025 16:37
optout21
optout21 previously approved these changes Jan 24, 2025
Copy link
Contributor

@optout21 optout21 left a comment

Choose a reason for hiding this comment

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

LGTM, OK to squash

@jkczyz jkczyz force-pushed the 2025-01-refactor-channel-phase branch from 22436bb to 6639f0c Compare January 24, 2025 23:05
@jkczyz
Copy link
Contributor Author

jkczyz commented Jan 24, 2025

Squashed. Looks like I need to rebase.

Now that ChannelPhase has been renamed, drop phase from related
identifiers.
Now that ChannelPhase has been renamed, drop phase from related
identifiers.
Now that ChannelPhase has been renamed, drop phase from related
identifiers.
Now that ChannelPhase has been renamed, drop phase from related
identifiers.
Now that ChannelPhase has been renamed, drop phase from related
identifiers.
Now that ChannelPhase has been renamed, drop phase from related
identifiers.
The old ChannelPhase variants will be used internally in Channel, so
they should no longer be used elsewhere.
Now that ChannelPhase has been renamed, drop phase from related
identifiers. Also, qualify uses of chan to avoid overloading the
identifier.
Instead of exposing the Channel enum variants, make a Channel struct
that wraps a ChannelPhase enum. This allows updating a Channel's phase
without consuming it, which isn't possible when it is in a map without
removing the entry first (e.g., as is done in ChannelManager).
When attempting a ChannelPhase transition, the variant-specific channel
struct needs to be taken by self in order to move its ChannelContext
into the struct for the new phase. Add a variant for an intermediate
state, allowing such actions.
When moving ChannelPhase logic from ChannelManager into Channel, it is
useful to error when a Channel is not in the expected state. Add a
ChannelError::SendError variant for this purpose, which results in
sending an error message without closing the channel.
Now that ChannelPhase is encapsulated in Channel, phase transitions can
be moved from ChannelManager to Channel. Update the funding_signed phase
transition accordingly. This allows for simpler logic in ChannelManager
since the channel does not need to removed and then readded into the
channel_by_id map.
Now that ChannelPhase is encapsulated in Channel, phase transitions can
be moved from ChannelManager to Channel. Update the tx_complete phase
transition accordingly. This allows for simpler logic in ChannelManager
since the channel does not need to removed and then re-added into the
channel_by_id map.
@jkczyz
Copy link
Contributor Author

jkczyz commented Jan 24, 2025

Rebased.

Copy link
Collaborator

@TheBlueMatt TheBlueMatt left a comment

Choose a reason for hiding this comment

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

Thanks

/// its variants containing an appropriate channel struct.
pub(super) enum Channel<SP: Deref> where SP::Target: SignerProvider {
enum ChannelPhase<SP: Deref> where SP::Target: SignerProvider {
Undefined,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Bleh, I almost prefer unsafe { mem::uninitialized }, oh well.

hash_map::Entry::Occupied(mut chan_entry) => {
let chan = chan_entry.get_mut();
match chan
.funding_signed(&msg, best_block, &self.signer_provider, &self.logger)
Copy link
Collaborator

Choose a reason for hiding this comment

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

We need to standardize whether wrapping the logger happens in channelmanager or channel and make sure we're consistent...at some point

// updates should be made on it. Previously, full_stack_target
// found an (unreachable) panic when the monitor update contained
// within `shutdown_finish` was applied.
chan.unset_funding_info();
Copy link
Collaborator

Choose a reason for hiding this comment

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

We removed debug_assert!(matches!(e, ChannelError::Close(_)) which I think is actually important here since we don't re-add the channel.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This may be ChannelError::SendError if the channel wasn't in the expected phase.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Err, right, duh, we're no longer removing-and-adding so this is fine.

@TheBlueMatt TheBlueMatt merged commit ed7befc into lightningdevkit:main Jan 27, 2025
24 of 25 checks passed
@jkczyz jkczyz mentioned this pull request Jan 29, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
weekly goal Someone wants to land this this week
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants