Skip to content

Commit 9d795ea

Browse files
committed
sweepbatcher: update feerate outside AddSweep
AddSweep may not be called after getting the first confirmation, but feerate updates are still needed in case of reorg. Update test TestFeeRateGrows not to call AddSweep again and make sure feerate is updated itself.
1 parent c59d902 commit 9d795ea

File tree

3 files changed

+99
-29
lines changed

3 files changed

+99
-29
lines changed

sweepbatcher/sweep_batch.go

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -169,9 +169,10 @@ type batchConfig struct {
169169
// initial delay completion and publishing the batch transaction.
170170
batchPublishDelay time.Duration
171171

172-
// noBumping instructs sweepbatcher not to fee bump itself and rely on
173-
// external source of fee rates (FeeRateProvider).
174-
noBumping bool
172+
// customFeeRate provides custom min fee rate per swap. The batch uses
173+
// max of the fee rates of its swaps. In this mode confTarget is
174+
// ignored and fee bumping by sweepbatcher is disabled.
175+
customFeeRate FeeRateProvider
175176

176177
// txLabeler is a function generating a transaction label. It is called
177178
// before publishing a batch transaction. Batch ID is passed to it.
@@ -723,6 +724,9 @@ func (b *batch) addSweeps(ctx context.Context, sweeps []*sweep) (bool, error) {
723724
// lower that minFeeRate of other sweeps (so it is
724725
// applied).
725726
if b.rbfCache.FeeRate < s.minFeeRate {
727+
b.Infof("Increasing feerate of the batch "+
728+
"from %v to %v", b.rbfCache.FeeRate,
729+
s.minFeeRate)
726730
b.rbfCache.FeeRate = s.minFeeRate
727731
}
728732
}
@@ -769,6 +773,9 @@ func (b *batch) addSweeps(ctx context.Context, sweeps []*sweep) (bool, error) {
769773
// Update FeeRate. Max(s.minFeeRate) for all the sweeps of
770774
// the batch is the basis for fee bumps.
771775
if b.rbfCache.FeeRate < s.minFeeRate {
776+
b.Infof("Increasing feerate of the batch "+
777+
"from %v to %v", b.rbfCache.FeeRate,
778+
s.minFeeRate)
772779
b.rbfCache.FeeRate = s.minFeeRate
773780
b.rbfCache.SkipNextBump = true
774781
}
@@ -968,6 +975,45 @@ func (b *batch) Run(ctx context.Context) error {
968975
continue
969976
}
970977

978+
// Update feerate of sweeps. This is normally done by
979+
// AddSweep, but it may not be called after the sweep
980+
// is confirmed, but fresh feerate is still needed to
981+
// keep publishing in case of reorg.
982+
for outpoint, s := range b.sweeps {
983+
minFeeRate, err := minimumSweepFeeRate(
984+
ctx, b.cfg.customFeeRate, b.wallet,
985+
s.swapHash, s.outpoint, s.confTarget,
986+
)
987+
if err != nil {
988+
b.Warnf("failed to determine feerate "+
989+
"for sweep %v of swap %x, "+
990+
"confTarget %d: %w", s.outpoint,
991+
s.swapHash[:6], s.confTarget,
992+
err)
993+
continue
994+
}
995+
996+
if minFeeRate <= s.minFeeRate {
997+
continue
998+
}
999+
1000+
b.Infof("Increasing feerate of sweep %v of "+
1001+
"swap %x from %v to %v", s.outpoint,
1002+
s.swapHash[:6], s.minFeeRate,
1003+
minFeeRate)
1004+
s.minFeeRate = minFeeRate
1005+
b.sweeps[outpoint] = s
1006+
1007+
if s.minFeeRate <= b.rbfCache.FeeRate {
1008+
continue
1009+
}
1010+
1011+
b.Infof("Increasing feerate of the batch "+
1012+
"from %v to %v", b.rbfCache.FeeRate,
1013+
s.minFeeRate)
1014+
b.rbfCache.FeeRate = s.minFeeRate
1015+
}
1016+
9711017
err := b.publish(ctx)
9721018
if err != nil {
9731019
return fmt.Errorf("publish error: %w", err)
@@ -1793,7 +1839,7 @@ func (b *batch) updateRbfRate(ctx context.Context) error {
17931839

17941840
// Set the initial value for our fee rate.
17951841
b.rbfCache.FeeRate = rate
1796-
} else if !b.cfg.noBumping {
1842+
} else if noBumping := b.cfg.customFeeRate != nil; !noBumping {
17971843
if b.rbfCache.SkipNextBump {
17981844
// Skip fee bumping, unset the flag, to bump next time.
17991845
b.rbfCache.SkipNextBump = false

sweepbatcher/sweep_batcher.go

Lines changed: 49 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1522,28 +1522,12 @@ func (b *Batcher) loadSweep(ctx context.Context, swapHash lntypes.Hash,
15221522

15231523
// Find minimum fee rate for the sweep. Use customFeeRate if it is
15241524
// provided, otherwise use wallet's EstimateFeeRate.
1525-
var minFeeRate chainfee.SatPerKWeight
1526-
if b.customFeeRate != nil {
1527-
minFeeRate, err = b.customFeeRate(ctx, swapHash, outpoint)
1528-
if err != nil {
1529-
return nil, fmt.Errorf("failed to fetch min fee rate "+
1530-
"for %x: %w", swapHash[:6], err)
1531-
}
1532-
if minFeeRate < chainfee.AbsoluteFeePerKwFloor {
1533-
return nil, fmt.Errorf("min fee rate too low (%v) for "+
1534-
"%x", minFeeRate, swapHash[:6])
1535-
}
1536-
} else {
1537-
if s.ConfTarget == 0 {
1538-
warnf("Fee estimation was requested for zero "+
1539-
"confTarget for sweep %x.", swapHash[:6])
1540-
}
1541-
minFeeRate, err = b.wallet.EstimateFeeRate(ctx, s.ConfTarget)
1542-
if err != nil {
1543-
return nil, fmt.Errorf("failed to estimate fee rate "+
1544-
"for %x, confTarget=%d: %w", swapHash[:6],
1545-
s.ConfTarget, err)
1546-
}
1525+
minFeeRate, err := minimumSweepFeeRate(
1526+
ctx, b.customFeeRate, b.wallet,
1527+
swapHash, outpoint, s.ConfTarget,
1528+
)
1529+
if err != nil {
1530+
return nil, err
15471531
}
15481532

15491533
return &sweep{
@@ -1567,11 +1551,53 @@ func (b *Batcher) loadSweep(ctx context.Context, swapHash lntypes.Hash,
15671551
}, nil
15681552
}
15691553

1554+
// feeRateEstimator determines feerate by confTarget.
1555+
type feeRateEstimator interface {
1556+
// EstimateFeeRate returns feerate corresponding to the confTarget.
1557+
EstimateFeeRate(ctx context.Context,
1558+
confTarget int32) (chainfee.SatPerKWeight, error)
1559+
}
1560+
1561+
// minimumSweepFeeRate determines minimum feerate for a sweep.
1562+
func minimumSweepFeeRate(ctx context.Context, customFeeRate FeeRateProvider,
1563+
wallet feeRateEstimator, swapHash lntypes.Hash, outpoint wire.OutPoint,
1564+
sweepConfTarget int32) (chainfee.SatPerKWeight, error) {
1565+
1566+
// Find minimum fee rate for the sweep. Use customFeeRate if it is
1567+
// provided, otherwise use wallet's EstimateFeeRate.
1568+
if customFeeRate != nil {
1569+
minFeeRate, err := customFeeRate(ctx, swapHash, outpoint)
1570+
if err != nil {
1571+
return 0, fmt.Errorf("failed to fetch min fee rate "+
1572+
"for %x: %w", swapHash[:6], err)
1573+
}
1574+
if minFeeRate < chainfee.AbsoluteFeePerKwFloor {
1575+
return 0, fmt.Errorf("min fee rate too low (%v) for "+
1576+
"%x", minFeeRate, swapHash[:6])
1577+
}
1578+
1579+
return minFeeRate, nil
1580+
}
1581+
1582+
if sweepConfTarget == 0 {
1583+
warnf("Fee estimation was requested for zero "+
1584+
"confTarget for sweep %x.", swapHash[:6])
1585+
}
1586+
minFeeRate, err := wallet.EstimateFeeRate(ctx, sweepConfTarget)
1587+
if err != nil {
1588+
return 0, fmt.Errorf("failed to estimate fee rate "+
1589+
"for %x, confTarget=%d: %w", swapHash[:6],
1590+
sweepConfTarget, err)
1591+
}
1592+
1593+
return minFeeRate, nil
1594+
}
1595+
15701596
// newBatchConfig creates new batch config.
15711597
func (b *Batcher) newBatchConfig(maxTimeoutDistance int32) batchConfig {
15721598
return batchConfig{
15731599
maxTimeoutDistance: maxTimeoutDistance,
1574-
noBumping: b.customFeeRate != nil,
1600+
customFeeRate: b.customFeeRate,
15751601
txLabeler: b.txLabeler,
15761602
customMuSig2Signer: b.customMuSig2Signer,
15771603
presignedHelper: b.presignedHelper,

sweepbatcher/sweep_batcher_test.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4838,8 +4838,6 @@ func testFeeRateGrows(t *testing.T, store testStore,
48384838
// Now update fee rate of second sweep (which is not primary) to
48394839
// feeRateHigh. Fee rate of sweep 1 is still feeRateLow.
48404840
setFeeRate(swapHash2, feeRateHigh)
4841-
require.NoError(t, batcher.AddSweep(ctx, &sweepReq1))
4842-
require.NoError(t, batcher.AddSweep(ctx, &sweepReq2))
48434841

48444842
// Tick tock next block.
48454843
err = lnd.NotifyHeight(603)

0 commit comments

Comments
 (0)