-
Notifications
You must be signed in to change notification settings - Fork 122
loopout: re-target sweep's feerate every block #838
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
loopout: re-target sweep's feerate every block #838
Conversation
292f1d6 to
6a58e56
Compare
hieblmi
left a comment
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.
First pass done. I left a question regarding the condition in SweepFeeRate.
sweeper/sweep_fee_provider.go
Outdated
| // UrgentSweepConfTarget instead of confTarget or uses the | ||
| // proportional approach to fees, not relying on confTarget. | ||
| } else { | ||
| // If the swap size is below a significant economical size, |
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.
don't we assume here that amt >= p.HighPrioSwapAmount but we could land here with the other condition?
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.
+1, the comments needs a little bit of update.
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.
Updated the comment:
} else {
// If the sweep size is below a significant economical size or
// the sweep expires in <=UrgentSweepConfTarget blocks, we'll
// obtain the fee we should use for the sweep based on our
// current best weight estimates as well as the confirmation
// target.
fee, feeRate, weight, err = p.Sweeper.GetSweepFeeDetails(
ctx, addInputToEstimator, destAddr, confTarget, label,
)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 was wrong in the sweep expires in <=UrgentSweepConfTarget blocks part. It checks confTarget, not the raw distance. ConfTarget is UrgentSweepConfTarget, when distance is DefaultSweepConfTargetDelta (which is larger).
I reworded the comment:
} else {
// If the sweep size is below a significant economical size or
// the sweep is not expiring soon, we'll obtain the fee we
// should use for the sweep based on our current best weight
// estimates as well as the confirmation target.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 code was removed from the PR.
bhandras
left a comment
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.
Looks good, just a few comments.
sweeper/sweep_fee_provider.go
Outdated
| // HighPrioSwapAmount is a threshold at which a swap is prioritized to | ||
| // be swept with a fee that is related to its size. The fee amount is | ||
| // determined by HighPrioFeePPM. | ||
| HighPrioSwapAmount btcutil.Amount |
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.
nit: given that this is now more general than just per swap fee we could rename this variable to HighPrioSweepAmount and generally "swap" to "sweep" in this file.
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.
Replaced "swap" with "sweep" everywhere in the package.
sweeper/sweep_fee_provider.go
Outdated
| // UrgentSweepConfTarget instead of confTarget or uses the | ||
| // proportional approach to fees, not relying on confTarget. | ||
| } else { | ||
| // If the swap size is below a significant economical size, |
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.
+1, the comments needs a little bit of update.
loopout.go
Outdated
| // greater or equal the size of highPrioSwapAmount to expedite the | ||
| // availability of on-chain funds to generate revenue on loop-out | ||
| // operations. | ||
| highPrioFeePPM = 50 |
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.
While this default is sane, I'd still recommend it (as well as high prio swap amount) to be configurable.
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.
+1
I'll do it. Also need to make sure high prio fee is not lower than a regular fee (which is more likely to happen given the "high prio" parameters will be configurable).
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 added a flag in SweepFeeProvider which disabled high prio logic altogether. It is enabled in Loop client. So we don't need options to configure these parameters.
0f153a7 to
27efdad
Compare
sweeper/log.go
Outdated
| var log btclog.Logger | ||
|
|
||
| // The default amount of logging is none. | ||
| func init() { |
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.
nit: similarly, add the sub logger in loopd/log.go.
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 removed sweeper package from the PR, so this logger doesn't exist anymore.
sweeper/sweep_fee_provider.go
Outdated
|
|
||
| // DisableHighPrioLogic disables special treatment of sweeps larger than | ||
| // or equal to HighPrioSweepAmount. | ||
| DisableHighPrioLogic bool |
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 he high prio part could be completely removed.
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.
Done. Now all the confTarget and feeRate logic is in method GetConfTargetAndFeeRate.
27efdad to
83afe80
Compare
|
I removed SweepFeeProvider and moved parts of the logic to Decided to keep urgent logic and floor logic. High prio logic is removed. New description of the change looks like this: Add type When determining confTarget, there are few adjustments over raw distance to cltv_expiry:
Also, if feerate is less than floor (1 sat/vbyte), then the floor is used.
Every block 100 sats/kw fee bump is disabled. Sweepbatcher re-targets feerate every block according to current mempool conditions and the number of blocks until expiry. Added tests for |
739fef6 to
2e571bf
Compare
bhandras
left a comment
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.
LGTM 🥇
hieblmi
left a comment
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 the concise calculation of the confTarget. LGTM 💾
loopout.go
Outdated
| // | ||
| // TODO(wilmer): tune? | ||
| DefaultSweepConfTargetDelta = DefaultSweepConfTarget * 2 | ||
| // swap's expiration height at which we begin to cap sweep confirmation |
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.
nit: ... at which be begin to cap the sweep...
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.
Fixed. Thanks!
loopout.go
Outdated
| // blocks to expire). | ||
| urgentSweepConfTarget = 3 | ||
|
|
||
| // urgentSweepConfTargetFactor is the factor we apply to fee-rate of |
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.
nit: feerate
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.
Fixed!
| // Assume the server is cooperative and we produce keyspend. | ||
| addInputToEstimator = func(e *input.TxWeightEstimator) error { | ||
| e.AddTaprootKeySpendInput(txscript.SigHashDefault) | ||
| return nil |
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.
nit: nl before return for readability
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.
Fixed
| var addInputToEstimator func(e *input.TxWeightEstimator) error | ||
| if canKeyspend { | ||
| // Assume the server is cooperative and we produce keyspend. | ||
| addInputToEstimator = func(e *input.TxWeightEstimator) error { |
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.
should we later add htlc.AddTaprootKeySpendInput() 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.
Looks good to me! I would call it htlc.AddCooperativeSuccessToEstimator to be more generic.
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.
Opened an issue: https://github.com/lightninglabs/nautilus/issues/1210
| // increase it. | ||
| newConfTarget := int32(urgentSweepConfTarget) | ||
| if confTarget < newConfTarget { | ||
| newConfTarget = confTarget |
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.
👍
| log.Infof("Got too low fee rate for swap %x: %v. Increasing "+ | ||
| "it to %v.", swapHash[:6], feeRate, minFeeRate) | ||
|
|
||
| feeRate = minFeeRate |
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.
👍
2e571bf to
a366205
Compare
|
@sputn1ck: review reminder |
|
I tested the PR in regtest simulating changing of feerate environment. It works! But I found a bug in the approved version of the PR: loopout.go calls AddSweep after sweepbatcher tries to sweep. The fee rate is updated by sweepbatcher reacting on AddSweep call, so this results in the fee rate being outdated by one block: In this example, the fee rate spiked from 500 sat/kw to 5000 sat/kw, but the old value (500 sat/kw) was used when making the sweep transaction. I fixed this by adding sweepbatcher.WithPublishDelay option to sweepbatcher. It sets the delay to 2 seconds. The delay in loopout.go before calling AddSweep is 1 second. Now the order is correct: Fee estimation (and possible update) now happens before the sweep transaction creation. I pushed the fix in a separate commit. I'll squash it with the previous commit before merging. |
hieblmi
left a comment
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.
Thanks for the investigation! LGTM.
The first result of function sweepConfTarget was not used. The function was renamed to canSweep to better reflect what it does.
There are some numeric constants that used to be defined as vars complicating their usage. They were turned into constants: MinLoopOutPreimageRevealDelta, DefaultSweepConfTarget, DefaultHtlcConfTarget, DefaultSweepConfTargetDelta. Also make liquidity.defaultHtlcConfTarget a constant, not a var.
Added argument 'label' to GetSweepFee and GetSweepFeeDetails. It is logged together with expected weight, fee and feerate.
Add type loopOutSweepFeerateProvider which determines confTarget based on distance to swap expiration, then determines feerate and fee using. Fee rate is plugged into sweepbatcher using WithCustomFeeRate. Option WithPublishDelay is used to make sure fee-rate is updated by loopout.go before the value is used by sweepbatcher. When determining confTarget, there are few adjustments over raw distance to cltv_expiry: - make sure confTarget is positive (if the swap has expired, raw distance is negative) - If confTarget is less than or equal to DefaultSweepConfTargetDelta (10), cap it with urgentSweepConfTarget and apply fee factor (1.1x). Also, if feerate is less than floor (1 sat/vbyte), then the floor is used. DefaultSweepConfTargetDelta was decreased from 18 to 10. Every block 100 sats/kw fee bump is disabled. Sweepbatcher re-targets feerate every block according to current mempool conditions and the number of blocks until expiry. Added tests for loopOutSweepFeerateProvider simulating various conditions.
235eb5e to
2f22f96
Compare
Add type
loopOutSweepFeerateProviderwhich determines confTarget based on distance to swap expiration, then determines feerate and fee using. Fee rate is plugged into sweepbatcher usingWithCustomFeeRate. Option WithPublishDelay is used to make sure fee-rate is updated by loopout.go before the value is used by sweepbatcher.When determining confTarget, there are few adjustments over raw distance to cltv_expiry:
DefaultSweepConfTargetDelta(10), cap it withurgentSweepConfTargetand apply fee factor (1.1x).Also, if feerate is less than floor (1 sat/vbyte), then the floor is used.
DefaultSweepConfTargetDeltawas decreased from 18 to 10.Every block 100 sats/kw fee bump is disabled. Sweepbatcher re-targets feerate every block according to current mempool conditions and the number of blocks until expiry.
Added tests for
loopOutSweepFeerateProvidersimulating various conditions.Pull Request Checklist
release_notes.mdif your PR contains major features, breaking changes or bugfixes