-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
WIP: make sweeper deadline aware #7549
WIP: make sweeper deadline aware #7549
Conversation
sweep/fee_bump.go
Outdated
|
||
// validateStrategy checks that a given strategy is defined. | ||
func validateStrategy(s Strategy) error { | ||
if s < strategySentinel { |
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: could use switch statement?
sweep/fee_bump.go
Outdated
case StrategyDeadlineAware: | ||
deadlineHeight := f.deadline | ||
|
||
target := deadlineHeight - currentHeight |
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 we need to watch for underflow here which would make target very large
sweep/fee_bump.go
Outdated
|
||
// If we are already behind deadline, fallback to conf target 2 | ||
// because that's minimal allowed value. | ||
if target < 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.
If target
= 1, then we're not technically behind the deadline as there is still 1 more block to go before the timelock expires.
Also, why is 2 the minimal value?
sweep/fee_bump.go
Outdated
} | ||
|
||
// decideConfTarget calculates a new conf target based on the strategy used. | ||
func (f *feeBumpParam) decideConfTarget(currentHeight uint32) uint32 { |
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 returns a conf target, but in low-fee environments I believe the conf target can give the same feerate. We want to change the feerate up to some maximum % of the HTLC value here. I think instead this should only use a feerate rather than a conf-target. This would mean passing in a fee-rate from the timeout resolver.
sweep/sweeper.go
Outdated
@@ -677,6 +695,16 @@ func (s *UtxoSweeper) collector(blockEpochs <-chan *chainntnfs.BlockEpoch) { | |||
} | |||
log.Tracef("input %v scheduled", outpoint) | |||
|
|||
if input.params.Strategy == StrategyDeadlineAware { | |||
err := s.handleDeadlineAwareInput( |
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.
Does this also need to be called up above when the input already exists in the sweeper?
sweep/sweeper.go
Outdated
params := &feeBumpParam{ | ||
strategy: StrategyDeadlineAware, | ||
initialConfTarget: initialConfTarget, | ||
deadline: uint32(currentHeight) + initialConfTarget, |
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 the deadline should be its own parameter because I think the current usage is that call-sites usually have a deadline but pass a conf target that is unrelated to the deadline. So adding the conf target to the current height might be too aggressive in some cases
sweep/sweeper.go
Outdated
}, | ||
} | ||
|
||
// TODO(yy): Inside a goroutine? |
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 this will block?
sweep/sweeper.go
Outdated
input: *op, | ||
params: ParamsUpdate{ | ||
Fee: FeePreference{ | ||
ConfTarget: 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.
missing the strategy field?
sweep/sweeper.go
Outdated
Fee: FeePreference{ | ||
ConfTarget: confTarget, | ||
}, | ||
Force: true, |
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.
think we can get rid of Force
, if it's uneconomical it's ok to give up
sweep/sweeper.go
Outdated
return nil | ||
} | ||
|
||
func (s *UtxoSweeper) handleFeeBump(currentHeight uint32) { |
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.
IMO this should only be for the deadline-aware inputs. The logic is a bit weird, but I think we can improve that later. We need to make sure that deadline-aware inputs don't stop after MaxSweepAttempts since they are going to be fee-bumping more since they are being re-submitted more in this function
0be3133
to
30b61da
Compare
This commit adds a new service, `feeBumper` along with its interface `Bumper`, to perform fee bumping over time sensitive inputs. This commit focuses on building the framework for the bumper, and the following commits will implement the actual strategies.
This commit adds a new strategy, `StrategyLinear`, to linearly increase the fee rate used for a given input when the deadline approaches.
30b61da
to
1308f3a
Compare
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.
Nice work porting this over from that other codebase! Left some inline comments, more specific to the lnd context. Main thing is that I think we can break up the new abstract strategies into an interface to further encapsulate the logic and allow for composition/re-use.
log btclog.Logger | ||
|
||
// feeLog is used by the fee bumper. | ||
feeLog btclog.Logger |
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.
Could make this a sub-package to have it get its own distinct logger, and also be useable as a dep-minimized sub-module.
|
||
// Generate a pkScript to be used by the fee bumper for weight | ||
// calculation. | ||
pkScript, err := s.cfg.GenSweepScript() |
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.
Can we alternatively just make something that's P2TR sized? This way we don't need to extend the look ahead gap each time we restart.
} | ||
|
||
// Add the input. | ||
if err := weightEstimate.add(m.Input); err != 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.
This doesn't seem to factor in the additional overhead of the type of inputs added to bump the transaction.
|
||
// Check if the request has already been processed. | ||
// | ||
// TODO(yy): allow overwrite? |
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.
Or return a nil
error unless we expect the caller to properly handle the returned ErrAlreadyMonitored
.
feeRateUsed = feeRate | ||
} | ||
|
||
// If the requst doesn't specify a broadcast height, use the manager's |
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.
requst -> request
var deadlineHeight uint32 | ||
|
||
// Calculate the deadline height if the conf target is set. | ||
if req.ConfTarget != NoHeightInfo { |
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.
Shouldn't this be ==
? Since in this case, no conf target was specified, so we need to enforce a default conf target.
f.monitoredInputs.Range(func(_ *wire.OutPoint, | ||
r *monitoredInput) bool { | ||
|
||
f.wg.Add(1) |
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 wonder if this should actually be done serially 🤔
Though with the way the sweeper works, all the goroutines will be serialized anyway, so it's nice to not have to block here so we can proceed other blocks/requests.
} | ||
|
||
// Strategy specifies how the fee bump is performed. | ||
type Strategy uint8 |
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.
A slightly different way to represent this would be to have Strategy
itself be an interface with methods for: seeing it things need to be bumped, and suggesting the proper fee rate. This would allow us to further encapsulate the logic of each strategy, and each strategy can also maintain its own state to make better decisions.
default: | ||
feeLog.Errorf("Unknown strategy %v", r.strategy) | ||
return r.currentFeeRate | ||
} | ||
} | ||
|
||
// TODO(yy): cache the results? | ||
func (f *feeBumper) feeRateForStrategyLinear( |
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.
If we make this part of the strategy interface, then we can easily add caching there (though seems to be a premature optimization at this point imo).
Superseded by #8667 |
This is a very raw PoC PR to make the sweeper deadline-aware and fixes #4215.
Depends on:
Aggregator
interface #8148In this PR, we extend the current sweeper to perform a fee bump if the input specifies it's deadline-aware. The sweeper will then continuously fee bump the sweeping tx in every new block if it's not confirmed.
An alternative approach would be fee bumping at the callsite, similar to how anchor sweeping is handled here. This approach IMO is not as good as the current one as a) the current approach has a deeper interface on sweeper and b) we can pre-define a set of fee bumping strategies so they can be easily applied based on specific conditions.
TODO: