-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Design Doc for Dynamic Commitments #10076
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
base: yy-dyn
Are you sure you want to change the base?
Conversation
This commit defines a new RPC endpoint that's used to initiate a dynamic commitment request.
In this commit we introduce a new package, `htlcswitch/dyn`, which is used to handle the dynamic commitment. This is made of its own package to stop growing the `htlcswitch` package, and debugging will be easier this way as we can now just turn on debug logs for `DYNC`.
In this commit, we introduce a new interface `Updater` that's used by the link to perform dynamic commitment updates to the link itself.
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.
Summary of Changes
Hello @yyforyongyu, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!
This pull request lays the groundwork for dynamic channel commitments in LND, enabling the modification of channel parameters without closing and reopening channels. It introduces a new RPC for user interaction and details the internal design choices for managing the complex negotiation and commitment update process, inviting feedback on the proposed architecture.
Highlights
- Design Proposal for Dynamic Commitments: This pull request serves as a design document for implementing dynamic commitments in LND, outlining the intent, proposed RPCs, and architectural considerations for updating channel parameters on the fly.
- New RPC: UpdateChannelParams: A new streaming RPC endpoint,
UpdateChannelParams
, is introduced. This RPC will allow users to initiate a negotiation to update various channel parameters (e.g., dust limit, max pending amount, channel reserve, min HTLC, CSV delay, max accepted HTLCs) in a user-friendly manner, abstracting away the underlying quiescence and commitment dance. - Architectural Discussion: Updater Interface: The PR proposes a new
Updater
interface withinhtlcswitch/dyn
to manage the dynamic commitment flow. It presents two main design approaches: a 'Monolith Approach' where theUpdater
handles all aspects (quiescence, negotiation, execution), and a 'Modulized Approach' where theUpdater
focuses only on negotiation, leveraging existing commitment dance logic. The author is seeking concept approval for the overall design direction. - New Logging Subsystem: A dedicated logging subsystem,
DYNC
, has been added underhtlcswitch/dyn
to provide specific logging for dynamic commitment operations.
Using Gemini Code Assist
The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.
Invoking Gemini
You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command>
or @gemini-code-assist <command>
. Below is a summary of the supported commands.
Feature | Command | Description |
---|---|---|
Code Review | /gemini review |
Performs a code review for the current pull request in its current state. |
Pull Request Summary | /gemini summary |
Provides a summary of the current pull request in its current state. |
Comment | @gemini-code-assist | Responds in comments when explicitly tagged, both in issue comments and review comments. |
Help | /gemini help |
Displays a list of available commands. |
Customization
To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/
folder in the base of the repository. Detailed instructions can be found here.
Limitations & Feedback
Gemini Code Assist is currently in preview and may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments to provide feedback.
You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.
Footnotes
-
Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩
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.
Code Review
This pull request introduces a design document and the initial scaffolding for dynamic channel commitments. This includes a new UpdateChannelParams
RPC and a new htlcswitch/dyn
package. The changes are well-structured and lay a good foundation for the new feature.
My review focuses on improving API design for long-term maintainability and ensuring strict adherence to the project's coding style guide. I've provided a few suggestions to make the new REST endpoint more conventional and to improve naming in the new protobuf messages. I've also pointed out a minor style guide violation in one of the new files.
// Subsystem defines the logging code for this subsystem. | ||
const Subsystem = "DYNC" | ||
|
||
// The default amount of logging is none. |
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.
According to the style guide, function comments should begin with the function name and be a complete sentence explaining the function's purpose.1
The init
function is a special function in Go, but it's good practice to document its purpose, especially in a library package.
Style Guide References
// The default amount of logging is none. | |
// init initializes a logger for this package. |
Footnotes
// Error message if the operation is failed, optional. | ||
// | ||
// This field should only be populated if the status indicates an error. | ||
string error = 2; |
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.
@@ -169,3 +169,5 @@ http: | |||
get: "/v1/aliases/list" | |||
- selector: lnrpc.Lightning.LookupHtlcResolution | |||
get: "/v1/htlc-resolution/{chan_id}/{htlc_index}" | |||
- selector: lnrpc.Lightning.UpdateChannelParams | |||
post: "/v1/channels/params/{channel_point.funding_txid_str}/{channel_point.output_index}" |
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 REST path /v1/channels/params/{...}
is a bit unconventional. It suggests that params
is a resource collection under /v1/channels
. A more RESTful approach would be to treat params
as a sub-resource of a specific channel.
Consider a path like /v1/channels/{channel_point.funding_txid_str}/{channel_point.output_index}/params
. This clearly indicates that you are updating the parameters of a specific channel.
While there are other inconsistencies in the existing API, this is a good opportunity to improve the design for new endpoints.
post: "/v1/channels/{channel_point.funding_txid_str}/{channel_point.output_index}/params"
This is something I consider to be pretty over due. I have a suuuper old version that I started way back here: Roasbeef@16d036e#diff-8896f76f365f8d3c7b6aec4e89140c923aeaf5a5ec1487b9c6cb9fd37cf5cab7R1. We'd ofc need to weigh the risks of such a refactor.
Can't we extract this into a distinct state machine? Perhaps using So then all the link does it forward to this state machine. The state machine can then just directly send a response to the peer if needed. Or invoke some other signal to do a commitment update (tho I think that'll auto trigger as the logs won't be 100% in sync after). So the terminal output in the success case is it returns something to insert into the log.
It doesn't need to access these directly. They can be accessed via interfaces ofc. If the option of decoupled message passing is attractive here (eg: to the
In my mental model, this happens within the state machine solely. This is part of the internal processing of the new log entry we insert. That gets updated in memory in the
Same as above, the job of the |
The number of blocks to use for the relative time lock in the | ||
pay-to-self output of both commitment transactions. | ||
*/ | ||
optional uint32 csv_delay = 6; |
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.
There's a bit of nuance here, as some of these fields are actually asymmetric when applied to a channel.
As an example, the csv_delay
we send in open_channel
is actually what we want them to use for their commitment transaction.
I think we can hide this fact from the user with this RPC, but it's something to keep in mind during the "negotiation" phase. As if we want to reduce our csv delay, we need to ask the remote peer if their willing to. This is security sensitive, as if the remote party just always approvals this subset of params, then I can get them to reduce it to 1 block, and try to breach (they have less time to react).
That's all to say that certain params can be auto accept, while others may need som edefalut setting or a sort of acceptor subscription.
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.
There's a bit of nuance here, as some of these fields are actually asymmetric when applied to a channel.
yeah good point, I'll update the docs to make them clearer.
That's all to say that certain params can be auto accept, while others may need som edefalut setting or a sort of acceptor subscription.
Def - I think we can reuse the validation logic used in channel opening.
Yeah that's what I've tried in the PoC. Will go with the Modulized Approach, and the job of the DynUpdater ends once we agree on the log entry to insert, then it's up to the link to perform the commitment dance. |
This PR is created to decide the design of dynamic commitments. It will be updated with a
README
file once finalized, and merged to the side branchyy-dyn
.A new RPC
UpdateChannelParams
A new RPC endpoint
UpdateChannelParams
is defined.UpgradeChannel
was chosen; however, given that it's also possible to downgrade channel types, and we are focusing on channel params here, a more descriptive name is chosen.rpcserver
, which is sent to theSwitch
. TheSwitch
will be responsible for finding the link and starting the process.TODO: We may consider moving channel-related operations into a new RPC server as the proto file is already huge, and it's difficult for LLMs to digest.
DynCommit
as an update logSimilar to how we process
update_fee
, thedyn_commit
, which is the agreed update from the dynamic commitment negotiation, will be treated as an update log and persisted on disk. Pro and cons,Pros,
Cons,
paymentDescriptor
- we could choose to use it still as-is, but it will be ugly and prone to bugs.htlcManager
, which can be hard to follow and debug. The commitment dance has many assumptions that are not made for dynamic commitments, such as there's no need to check for HTLCs or circuits when processing the commitment update in dyn. There's also no need to use thePendingCommitTicker
as quiescence has its own timeout logic. Finally, to exit quiescence in this mixed flow is not as clear as we process all dyn-related operations in one place.Interface
Updater
A new interface,
Updater
, is introduced underhtlcswitch/dyn
, and is accessed bychannelLink
. When an RPC request or adyn_propose
msg is received, the link will invokeUpdater
to start the dynamic commitment flow. The overall flow is shown in the following diagram:There are three main tasks,
dyn_propose
,dyn_ack
anddyn_reject
.dyn_commit
,commit_sig
andrevoke_and_ack
. Basically it's a simplified commitment dance.Based on the scope of
Updater
's responsibility, we have two ways to design it.Monolith Approach
We can view the dynamic commitment as a whole and let
Updater
manage the above three tasks, which was tried in the PoC. This approach is nice as it gives an almost linear flow of the process, making it easier to understand where we are in the flow. It should be easier to maintain and debug. However, given that the dyn execution is just a simplified commitment dance, it does mean we won't take advantage of the existing commitment dance flow. It also meansDynUpdater
needs access to several subsystems,Quiescer
to initiate and terminate quiescence.lnpeer.Peer
to send dyn-related msgs.*lnwallet.LightningChannel
to update the channel config (ChannelConfig
).*lnwallet.LightningChannel
to persistDynCommit
on disk,ReceiveDynCommit
SendDynCommit
*lnwallet.LightningChannel
to process thecommit_sig
andrevoke_and_ack
,ReceiveNewCommitment
SignNextCommitment
RevokeCurrentCommitment
ReceiveRevocation
TowerClient
to backup the state.Modulized Approach
The
Updater
will only be responsible for the negotiation task. The link will handle quiescence initialization and termination, and its current commitment dance will be used for thedyn_commit
. This approach aligns with the idea of treatingdyn_commit
as an update log; it's also nice as theUpdater
only needs to have access tolnpeer.Peer
to send dyn-related msgs, as the rest of the operations are handled in the link.This does mean we may lose some clarity about the overflow of the dynamic commitment. The link also needs to be stateful so it knows we are in the middle of a dynamic commitment.
Gemini's view
Gemini favors the Monolith approach even after I offered to refactor
htlcManager
to have a cleaner commitment update flow, here's the full text. The conclusion is,Looking for a concept ACK here - maybe Gemini will think otherwise if the
htlcManager
has a cleaner flow. Personally I'm leaning towards Modulized approach, since we want to have a generalized control flow in the link to allow not just quiescence, dynamic commitments, but also splicing, etc.