-
Notifications
You must be signed in to change notification settings - Fork 2.2k
lnwallet/chancloser: remove the commit fee clamp, introduce max fee #6770
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
Changes from 1 commit
c140da8
4a997bf
106912b
9eb1e87
c791b18
1e556aa
046aa7f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -35,6 +35,12 @@ var ( | |
| // shutdown script previously set for that party. | ||
| ErrUpfrontShutdownScriptMismatch = fmt.Errorf("shutdown script does not " + | ||
| "match upfront shutdown script") | ||
|
|
||
| // ErrProposalExeceedsMaxFee is returned when as the initiator, the | ||
| // latest fee proposal sent by the responder exceed our max fee. | ||
| // responder. | ||
| ErrProposalExeceedsMaxFee = fmt.Errorf("latest fee proposal exceeds " + | ||
| "max fee") | ||
| ) | ||
|
|
||
| // closeState represents all the possible states the channel closer state | ||
|
|
@@ -73,6 +79,14 @@ const ( | |
| closeFinished | ||
| ) | ||
|
|
||
| const ( | ||
| // defaultMaxFeeMultiplier is a multiplier we'll apply to the ideal fee | ||
| // of the initiator, to decide when the negotiated fee is too high. By | ||
| // default, we want to bail out if we attempt to negotiate a fee that's | ||
| // 3x higher than our max fee. | ||
| defaultMaxFeeMultiplier = 3 | ||
| ) | ||
|
|
||
| // ChanCloseCfg holds all the items that a ChanCloser requires to carry out its | ||
| // duties. | ||
| type ChanCloseCfg struct { | ||
|
|
@@ -89,6 +103,9 @@ type ChanCloseCfg struct { | |
| // Disconnect will disconnect from the remote peer in this close. | ||
| Disconnect func() error | ||
|
|
||
| // MaxFee, is non-zero represents the highest fee that the initiator is | ||
| // willing to pay to close the channel. | ||
| MaxFee chainfee.SatPerKWeight | ||
| // Quit is a channel that should be sent upon in the occasion the state | ||
| // machine should cease all progress and shutdown. | ||
| Quit chan struct{} | ||
|
|
@@ -122,6 +139,11 @@ type ChanCloser struct { | |
| // offer when starting negotiation. This will be used as a baseline. | ||
| idealFeeSat btcutil.Amount | ||
|
|
||
| // maxFee is the highest fee the initiator is willing to pay to close | ||
| // out the channel. This is either a use specified value, or a default | ||
| // multiplier based of the initial starting ideal fee. | ||
| maxFee btcutil.Amount | ||
|
|
||
| // lastFeeProposal is the last fee that we proposed to the remote party. | ||
| // We'll use this as a pivot point to ratchet our next offer up, down, or | ||
| // simply accept the remote party's prior offer. | ||
|
|
@@ -168,17 +190,12 @@ func NewChanCloser(cfg ChanCloseCfg, deliveryScript []byte, | |
| // TODO(roasbeef): should factor in minimal commit | ||
| idealFeeSat := cfg.Channel.CalcFee(idealFeePerKw) | ||
|
|
||
| // If this fee is greater than the fee currently present within the | ||
| // commitment transaction, then we'll clamp it down to be within the proper | ||
| // range. | ||
| // | ||
| // TODO(roasbeef): clamp fee func? | ||
| channelCommitFee := cfg.Channel.StateSnapshot().CommitFee | ||
| if idealFeeSat > channelCommitFee { | ||
| chancloserLog.Infof("Ideal starting fee of %v is greater than commit "+ | ||
| "fee of %v, clamping", int64(idealFeeSat), int64(channelCommitFee)) | ||
|
|
||
| idealFeeSat = channelCommitFee | ||
| // When we're the initiator, we'll want to also factor in the highest | ||
| // fee we want to pay. This'll either be 3x the ideal fee, or the | ||
| // specified explicit max fee. | ||
| maxFee := idealFeeSat * defaultMaxFeeMultiplier | ||
| if cfg.MaxFee > 0 { | ||
| maxFee = cfg.Channel.CalcFee(cfg.MaxFee) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similar to the ideal fee, the max fee isn't respected on restart
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, I think one way to resolve this would be to commit this information to disk when we call
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So we'd store persistently the preferences of the user at that point, and for the case where we're the responder (but still the initiator), we'd do something like base it off some (larger?) multiplier of the max commitment fee.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think it should be stored in I also think we could check here that the ideal fee isn't greater than the max fee if one exists
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
and/or a config option?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So from my reading, we'll retain the "ideal" fee on restart, since we'll compute the "ideal" fee based on So I think we can either leave it as is, persist it in #6760, or add a config option (yet another one, that ppl prob won't set tbh) to let ppl express what this max multiplier should be.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are two ways the ideal fee is set:
The first case probably won't have the same fee on restart. The second may if the fee estimation doesn't change. I don't think this should be persisted in this PR. It could be done in #6760. But I think any persistence changes should be done in the same release so we don't have to check whether something is stored or not
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. SGTM. I'd say a user's expectation would be for things to resume with their desired fee, so we should strive for that. Agree we can do it in another PR, though rn #6760 is marked for 0.16 (and in a draft state). |
||
| } | ||
|
|
||
| chancloserLog.Infof("Ideal fee for closure of ChannelPoint(%v) is: %v sat", | ||
|
|
@@ -193,6 +210,7 @@ func NewChanCloser(cfg ChanCloseCfg, deliveryScript []byte, | |
| cfg: cfg, | ||
| negotiationHeight: negotiationHeight, | ||
| idealFeeSat: idealFeeSat, | ||
| maxFee: maxFee, | ||
| localDeliveryScript: deliveryScript, | ||
| priorFeeOffers: make(map[btcutil.Amount]*lnwire.ClosingSigned), | ||
| locallyInitiated: locallyInitiated, | ||
|
|
@@ -483,9 +501,10 @@ func (c *ChanCloser) ProcessCloseMsg(msg lnwire.Message) ([]lnwire.Message, | |
| feeProposal := calcCompromiseFee(c.chanPoint, c.idealFeeSat, | ||
| c.lastFeeProposal, remoteProposedFee, | ||
| ) | ||
| if feeProposal > c.idealFeeSat*3 { | ||
| return nil, false, fmt.Errorf("couldn't find" + | ||
| " compromise fee") | ||
| if c.cfg.Channel.IsInitiator() && feeProposal > c.maxFee { | ||
| return nil, false, fmt.Errorf("%w: %v > %v", | ||
| ErrProposalExeceedsMaxFee, feeProposal, | ||
| c.maxFee) | ||
| } | ||
|
|
||
| // With our new fee proposal calculated, we'll craft a new close | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.