Skip to content
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

[3/4] - lnwallet/chancloser: add new protofsm based RBF chan closer #8512

Merged
merged 13 commits into from
Dec 10, 2024

Conversation

Roasbeef
Copy link
Member

@Roasbeef Roasbeef commented Mar 1, 2024

Overview

In this PR, we add a new RBF based co-op channel close state machine. This uses the new protofsm package to define the states and transitions for the state machine. The new state machine is then also updated to become a peer.MsgEndpoint which allows us to use the new peer.MsgRouter to handle all message dispatch into the state machine. A series of new wrapper structs (taking care to always accept interfaces to decouple from the concrete types) are created to be able to initialize the environment needed by the new state machine.

This PR doesn't yet include integration within the peer struct. That will come in a follow up PR as we need some additional changes to be able to handle the two possible co-op types that will exist.

The diff might look somewhat large, but over half of it us unit tests, and a large portion of the non-test diff is the new state and event declarations.

State Machine Flow

The new state machine can be modeled with the following diagram:

---
title: Co-Op Close V2
---
stateDiagram-v2
    state CoopCloseV2 {
    [*] --> ChannelActive
    ChannelActive --> ShutdownPending: send_shutdown
    ChannelActive --> ShutdownPending: shutdown_received

	ShutdownPending --> ChannelFlushing: shutdown_received
    ShutdownPending --> ChannelFlushing: shutdown_complete
    
    ChannelFlushing --> ClosingNegotiation: channel_flushed

    state ClosingNegotiation {
		direction TB
        [*] --> LocalCloseStart
        [*] --> RemoteCloseStart
        
        LocalCloseStart --> LocalOfferSent: send_offer
        LocalOfferSent --> ClosePending: local_sig_received
        
        RemoteCloseStart --> ClosePending: offer_received
    }
    
    ClosingNegotiation --> ShutdownPending: send_shutdown
    ClosingNegotiation --> ShutdownPending: shutdown_received
   
    ClosingNegotiation --> CloseFin: txn_confirmation
    }
Loading

Remaining TODOs

  • unit tests for the state machine
  • commit the above diagram and a msg flow diagram to repo as documentation

Fixes #7091

@Roasbeef Roasbeef added channel closing Related to the closing of channels cooperatively and uncooperatively channel management The management of the nodes channels spec rbf labels Mar 1, 2024
@Roasbeef Roasbeef added this to the v0.18.0 milestone Mar 1, 2024
@Roasbeef Roasbeef requested review from yyforyongyu and Crypt-iQ March 1, 2024 01:45
Copy link
Contributor

coderabbitai bot commented Mar 1, 2024

Important

Review skipped

Auto reviews are limited to specific labels.

🏷️ Labels to auto review (1)
  • llm-review

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Experiment)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

github-actions bot commented Mar 1, 2024

Pull reviewers stats

Stats of the last 30 days for lnd:

User Total reviews Time to review Total comments
guggero
🥇
14
▀▀▀
1d 3h 52m
26
▀▀
yyforyongyu
🥈
9
▀▀
1d 16h 25m
8
ellemouton
🥉
8
▀▀
4d 8h 7m
25
▀▀
ProofOfKeags
6
1d 16h 3m
42
▀▀▀▀
bhandras
5
4d 10h 42m
5
Crypt-iQ
3
1d 21h 30m
8
Roasbeef
2
12h 16m
3
saubyk
2
8h 12m
0
morehouse
1
14d 21h 46m
▀▀▀▀
0
ziggie1984
1
10d 15h 59m
▀▀▀
2

@saubyk
Copy link
Collaborator

saubyk commented Mar 1, 2024

Hi @Roasbeef can you please explain the scenario where you're expecting the state to transition back from ClosingNegotiation to ShutdownPending?

Given that the channel flushing has already happened before the state moved to ClosingNegotiation, what would be the scenario where that step would need to be executed again?

@Roasbeef
Copy link
Member Author

Roasbeef commented Mar 1, 2024

@saubyk good question, so I need to update the diagram slightly, but you're right in that at that point the channel is already flushed. With the code that's checked in, there's a special fast path that'll transition from ChannelActive all the way to ClosingNegotiation if we detect that the channel is already flushed. It'll already be flushed if we're doing an RBF iteration, or if we're reconnecting and want to resume the closing process.

Here's the location of one of the fast path transitions:

So in that case we sent shutdown, received it, then rather than wait in the ChannelFlushing state, we'll emit an internal event to have us go directly to the ChannelNegotiation phase.

Also just to clarify, the way the protocol works is that when either side wants to do a new RBF iteration (RBF their closing txn), they send the shutdown message again. You effectively can drop all state, then act as if you're closing for the first time. Reading between the lines, I think yuo're right in that you could just send the signatures again, but perhaps one side wants to close to a new address, and that information is contained within teh shutdown message.

I kept things like this rather than introducing some new states to keep the amount of total states to a minimum, and re-use more of the existing transition logic. As an example, if they send a shutdown message to us, we still want to validate that they're using the same upfront shutdown address, etc.

@Roasbeef Roasbeef changed the base branch from peer-msg-router to protofsm March 5, 2024 05:56
@Roasbeef Roasbeef force-pushed the protofsm branch 2 times, most recently from fa12732 to 578288e Compare March 6, 2024 03:13
@Roasbeef Roasbeef changed the title lnwallet/chancloser: add new protofsm based RBF chan closer [3/4] -lnwallet/chancloser: add new protofsm based RBF chan closer Mar 8, 2024
@Roasbeef Roasbeef changed the title [3/4] -lnwallet/chancloser: add new protofsm based RBF chan closer [3/4] - lnwallet/chancloser: add new protofsm based RBF chan closer Mar 8, 2024
@saubyk saubyk modified the milestones: v0.18.0, v0.18.1 Mar 21, 2024
Copy link
Collaborator

@ProofOfKeags ProofOfKeags left a comment

Choose a reason for hiding this comment

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

First pass. This seems to implement the CCV2 protocol as I understand it, without issue. I did find the code hard to review though due to having to track the semantics of protofsm. I believe aspects of protofsm can be simplified specifically in how it pertains to emitting events.

In my ideal world we would make simplifying protofsm a prerequisite for merging this, but as I understand it, the deadline trumps fixing protofsm? I'd like others' input here. What I like here is that all of the state transitions are very clearly pure and I don't have any difficulty tracking whether or not the implementation of the states/state transitions is valid. However, some of the other operational semantics in how it ferries events out of it into the surrounding context, specifically as it pertains to internal events, can be complex.

lnwallet/chancloser/rbf_coop_states.go Outdated Show resolved Hide resolved
lnwallet/chancloser/rbf_coop_transitions.go Outdated Show resolved Hide resolved
lnwallet/chancloser/rbf_coop_transitions.go Show resolved Hide resolved
lnwallet/chancloser/chancloser.go Show resolved Hide resolved
lnwallet/chancloser/rbf_coop_transitions.go Outdated Show resolved Hide resolved
lnwallet/chancloser/rbf_coop_transitions.go Outdated Show resolved Hide resolved
lnwallet/chancloser/rbf_coop_transitions.go Show resolved Hide resolved
lnwallet/chancloser/rbf_coop_transitions.go Show resolved Hide resolved
lnwallet/chancloser/rbf_coop_msg_mapper.go Show resolved Hide resolved
lnwallet/chancloser/rbf_coop_test.go Outdated Show resolved Hide resolved
@saubyk saubyk added the P1 MUST be fixed or reviewed label Jun 25, 2024
@saubyk saubyk modified the milestones: v0.18.3, v0.19.0 Aug 1, 2024
@Roasbeef
Copy link
Member Author

Updated to point to master.

Copy link
Collaborator

@Crypt-iQ Crypt-iQ left a comment

Choose a reason for hiding this comment

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

Still need to grok the state transitions a bit more

lnwallet/chancloser/rbf_coop_states.go Outdated Show resolved Hide resolved
lnwallet/chancloser/rbf_coop_transitions.go Outdated Show resolved Hide resolved
lnwallet/chancloser/rbf_coop_transitions.go Outdated Show resolved Hide resolved
lnwallet/chancloser/rbf_coop_transitions.go Show resolved Hide resolved
lnwallet/chancloser/rbf_coop_transitions.go Outdated Show resolved Hide resolved
lnwallet/chancloser/rbf_coop_transitions.go Show resolved Hide resolved

// genChannelType generates various channel types, ensuring good coverage of
// different channel configurations including anchor outputs and other features
func genChannelType(t *rapid.T) channeldb.ChannelType {
Copy link
Collaborator

Choose a reason for hiding this comment

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

From the rapid go page:

Compared to [testing.F.Fuzz](https://pkg.go.dev/testing#F.Fuzz), rapid shines in generating complex
structured data, including state machine tests, but lacks coverage-guided feedback and mutations. Note
that with [MakeFuzz](https://pkg.go.dev/pgregory.net/rapid#MakeFuzz), any rapid test can be used as a 
fuzz target for the standard fuzzer.

It would be good to convert all of our rapid tests to fuzz tests. Rapid would be used to generate the initial data (I'm guessing it would be better than go's fuzzing engine for that) and then the fuzzer would mutate off of that.

lnwallet/close_test.go Show resolved Hide resolved
@lightninglabs-deploy
Copy link

@Roasbeef, remember to re-request review from reviewers when ready

Copy link
Collaborator

@ProofOfKeags ProofOfKeags left a comment

Choose a reason for hiding this comment

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

I don't have many additional comments besides the ones I left previously.

lnwallet/channel.go Show resolved Hide resolved
lnwallet/chancloser/rbf_coop_transitions.go Outdated Show resolved Hide resolved
lnwallet/chancloser/rbf_coop_transitions.go Show resolved Hide resolved
lnwallet/close_test.go Outdated Show resolved Hide resolved
lnwallet/close_test.go Show resolved Hide resolved
lntypes/channel_party.go Outdated Show resolved Hide resolved
@Crypt-iQ
Copy link
Collaborator

Crypt-iQ commented Dec 4, 2024

Haven't looked too much into 4/4 yet, so I'm not sure if these items are addressed there. I do think the following should be addressed either here or in the next PR (especially points 2 & 3):

Besides that the PR looks good and can approve once CI passes

@Roasbeef
Copy link
Member Author

Roasbeef commented Dec 5, 2024

Still need to grok the state transitions a bit more

Gotcha, the diagram in the OP should still be up to date.

@Roasbeef
Copy link
Member Author

Roasbeef commented Dec 5, 2024

apparently, sometimes the remote closing_complete is allowed to be invalid? See this flow from the BOLT here where Alice ignores Bob's closing_complete: https://github.com/lightning/bolts/pull/1205/files#diff-ed04ca2c673fd6aabde69389511fa9ee60cb44d6b2ef6c88b549ffaa753d6afeR1540-R1554

Looks like that's trying to handle a case where Alice has sent a shutdown with one script, but then wants to switch to another. I don't quite get that example though, Alice sent the script using the shutdown so she much send another one (after the round) if she wants to swap her script. Will reply on the spec.

EDIT: commented about that here https://github.com/lightning/bolts/pull/1205/files#r1871743799

@Roasbeef
Copy link
Member Author

Roasbeef commented Dec 5, 2024

in addition to wire fuzz tests, I think we should have a fuzzer for the actual state machine introduced here where we can feed it random events.

So the rapid package has an API meant allow testing of state machines: https://pkg.go.dev/pgregory.net/rapid#T.Repeat

Not super familiar with it yet, but can look into adapting it, may not play will with the way protofsm is set up rn. Alternatively I can look into adding additional test coverage to each state transition using rapid, but with the fuzzing transformation on.

We'll have the empty slice tuple represent the None case instead.
In this commit, we add the initial set of states for the new protofsm
based rbf chan closer. A diagram outlining the new states and their
transitions can be found here:
https://gist.github.com/Roasbeef/acc4ff51b9dff127230228a05553cdfe.

Unlike the existing co-op close process, this co-op close can be
restarted at anytime if either side sends a shutdown message. From
there, we'll each obtain a new RBF'd version that can be re-broadcasted.

This commit creates the set of states, along with the environment that
our state machine will use to drive itself forward.
In this commit, we add the ability to specify a custom sequence for a
co-op close tx. This'll come in handy later as the new co-op close
process allows a party to set a custom sequence.
In this commit, we add the state transitions for the new protofsm based
RBF chan closer. The underlying protocol is a new asymmetric co-op close
process, wherein either side can initiate a chan closer, and use their
settled funds to pay for fees within the channel.
This'll allow us to treat the state machine as a MsgEndpoint, and have
the readHandler in the peer automatically send new messages to it.
This preps us for an upcoming change to the rbf coop state machine where
either party can pay for the channel fees. We also add a new test to
make sure the new function adheres to some key properties.
In this commit, we update the core coop close logic with the new custom
payer param. We also expand the existing unit tests to ensure that the
fee is deducted from the proper party.
In this commit, we enable a custom payer for the rbf coop close. This
allows us to ensure that the party that started one side of the close
flow pays the fees.
@Roasbeef Roasbeef merged commit d6eeaec into master Dec 10, 2024
19 of 33 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
channel closing Related to the closing of channels cooperatively and uncooperatively channel management The management of the nodes channels no-changelog P1 MUST be fixed or reviewed rbf spec
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

[bug]: unable to retry coop close attempt with higher fee rate (between two nodes running v0.15.3)
5 participants