mirror of
https://github.com/lightninglabs/loop.git
synced 2026-08-13 12:33:03 +02:00
sweepbatcher: add field SweepInfo.MinFeeRate
MinFeeRate is minimum fee rate that must be used by a batch of the sweep. If it is specified, confTarget is ignored. This is useful for external source of fees.
This commit is contained in:
parent
4e085f11a3
commit
fd971d4951
3 changed files with 90 additions and 23 deletions
|
|
@ -97,6 +97,10 @@ type sweep struct {
|
|||
// notifier is a collection of channels used to communicate the status
|
||||
// of the sweep back to the swap that requested it.
|
||||
notifier *SpendNotifier
|
||||
|
||||
// minFeeRate is minimum fee rate that must be used by a batch of
|
||||
// the sweep. If it is specified, confTarget is ignored.
|
||||
minFeeRate chainfee.SatPerKWeight
|
||||
}
|
||||
|
||||
// batchState is the state of the batch.
|
||||
|
|
@ -399,9 +403,10 @@ func (b *batch) addSweep(ctx context.Context, sweep *sweep) (bool, error) {
|
|||
b.sweeps[sweep.swapHash] = *sweep
|
||||
|
||||
// If this is the primary sweep, we also need to update the
|
||||
// batch's confirmation target.
|
||||
// batch's confirmation target and fee rate.
|
||||
if b.primarySweepID == sweep.swapHash {
|
||||
b.cfg.batchConfTarget = sweep.confTarget
|
||||
b.rbfCache.FeeRate = sweep.minFeeRate
|
||||
}
|
||||
|
||||
return true, nil
|
||||
|
|
@ -443,6 +448,7 @@ func (b *batch) addSweep(ctx context.Context, sweep *sweep) (bool, error) {
|
|||
if b.primarySweepID == lntypes.ZeroHash {
|
||||
b.primarySweepID = sweep.swapHash
|
||||
b.cfg.batchConfTarget = sweep.confTarget
|
||||
b.rbfCache.FeeRate = sweep.minFeeRate
|
||||
|
||||
// We also need to start the spend monitor for this new primary
|
||||
// sweep.
|
||||
|
|
@ -456,6 +462,12 @@ func (b *batch) addSweep(ctx context.Context, sweep *sweep) (bool, error) {
|
|||
b.log.Infof("adding sweep %x", sweep.swapHash[:6])
|
||||
b.sweeps[sweep.swapHash] = *sweep
|
||||
|
||||
// Update FeeRate. Max(sweep.minFeeRate) for all the sweeps of
|
||||
// the batch is the basis for fee bumps.
|
||||
if b.rbfCache.FeeRate < sweep.minFeeRate {
|
||||
b.rbfCache.FeeRate = sweep.minFeeRate
|
||||
}
|
||||
|
||||
return true, b.persistSweep(ctx, *sweep, false)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -120,6 +120,10 @@ type SweepInfo struct {
|
|||
|
||||
// DestAddr is the destination address of the sweep.
|
||||
DestAddr btcutil.Address
|
||||
|
||||
// MinFeeRate is minimum fee rate that must be used by a batch of
|
||||
// the sweep. If it is specified, confTarget is ignored.
|
||||
MinFeeRate chainfee.SatPerKWeight
|
||||
}
|
||||
|
||||
// SweepFetcher is used to get details of a sweep.
|
||||
|
|
@ -523,6 +527,9 @@ func (b *Batcher) spinUpBatchFromDB(ctx context.Context, batch *batch) error {
|
|||
|
||||
sweeps := make(map[lntypes.Hash]sweep)
|
||||
|
||||
// Collect feeRate from sweeps and stored batch.
|
||||
feeRate := batch.rbfCache.FeeRate
|
||||
|
||||
for _, dbSweep := range dbSweeps {
|
||||
sweep, err := b.convertSweep(ctx, dbSweep)
|
||||
if err != nil {
|
||||
|
|
@ -530,11 +537,16 @@ func (b *Batcher) spinUpBatchFromDB(ctx context.Context, batch *batch) error {
|
|||
}
|
||||
|
||||
sweeps[sweep.swapHash] = *sweep
|
||||
|
||||
// Set minFeeRate to max(sweep.minFeeRate) for all sweeps.
|
||||
if feeRate < sweep.minFeeRate {
|
||||
feeRate = sweep.minFeeRate
|
||||
}
|
||||
}
|
||||
|
||||
rbfCache := rbfCache{
|
||||
LastHeight: batch.rbfCache.LastHeight,
|
||||
FeeRate: batch.rbfCache.FeeRate,
|
||||
FeeRate: feeRate,
|
||||
}
|
||||
|
||||
logger := batchPrefixLogger(fmt.Sprintf("%d", batch.id))
|
||||
|
|
@ -758,6 +770,7 @@ func (b *Batcher) convertSweep(ctx context.Context, dbSweep *dbSweep) (
|
|||
protocolVersion: s.ProtocolVersion,
|
||||
isExternalAddr: s.IsExternalAddr,
|
||||
destAddr: s.DestAddr,
|
||||
minFeeRate: s.MinFeeRate,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
@ -855,5 +868,6 @@ func (b *Batcher) fetchSweep(ctx context.Context,
|
|||
protocolVersion: s.ProtocolVersion,
|
||||
isExternalAddr: s.IsExternalAddr,
|
||||
destAddr: s.DestAddr,
|
||||
minFeeRate: s.MinFeeRate,
|
||||
}, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import (
|
|||
"github.com/lightningnetwork/lnd/chainntnfs"
|
||||
"github.com/lightningnetwork/lnd/keychain"
|
||||
"github.com/lightningnetwork/lnd/lntypes"
|
||||
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
|
@ -1746,20 +1747,64 @@ func testSweepFetcher(t *testing.T, store testStore,
|
|||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
swapHash := lntypes.Hash{1, 1, 1}
|
||||
|
||||
// Provide min fee rate for the sweep.
|
||||
feeRate := chainfee.SatPerKWeight(30000)
|
||||
amt := btcutil.Amount(1_000_000)
|
||||
weight := lntypes.WeightUnit(445) // Weight for 1-to-1 tx.
|
||||
bumpedFee := feeRate + 100
|
||||
expectedFee := bumpedFee.FeeForWeight(weight)
|
||||
|
||||
swap := &loopdb.LoopOutContract{
|
||||
SwapContract: loopdb.SwapContract{
|
||||
CltvExpiry: 222,
|
||||
AmountRequested: amt,
|
||||
ProtocolVersion: loopdb.ProtocolVersionMuSig2,
|
||||
HtlcKeys: loopdb.HtlcKeys{
|
||||
SenderScriptKey: senderKey,
|
||||
ReceiverScriptKey: receiverKey,
|
||||
SenderInternalPubKey: senderKey,
|
||||
ReceiverInternalPubKey: receiverKey,
|
||||
},
|
||||
},
|
||||
DestAddr: destAddr,
|
||||
SwapInvoice: swapInvoice,
|
||||
SweepConfTarget: 321,
|
||||
}
|
||||
|
||||
htlc, err := utils.GetHtlc(
|
||||
swapHash, &swap.SwapContract, lnd.ChainParams,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
sweepInfo := &SweepInfo{
|
||||
ConfTarget: 123,
|
||||
Timeout: 111,
|
||||
SwapInvoicePaymentAddr: *swapPaymentAddr,
|
||||
MinFeeRate: feeRate,
|
||||
ProtocolVersion: loopdb.ProtocolVersionMuSig2,
|
||||
HTLCKeys: loopdb.HtlcKeys{
|
||||
SenderScriptKey: senderKey,
|
||||
ReceiverScriptKey: receiverKey,
|
||||
SenderInternalPubKey: senderKey,
|
||||
ReceiverInternalPubKey: receiverKey,
|
||||
},
|
||||
HTLC: *htlc,
|
||||
HTLCSuccessEstimator: htlc.AddSuccessToEstimator,
|
||||
DestAddr: destAddr,
|
||||
}
|
||||
|
||||
sweepFetcher := &sweepFetcherMock{
|
||||
store: map[lntypes.Hash]*SweepInfo{
|
||||
{1, 1, 1}: {
|
||||
ConfTarget: 123,
|
||||
Timeout: 111,
|
||||
SwapInvoicePaymentAddr: *swapPaymentAddr,
|
||||
},
|
||||
swapHash: sweepInfo,
|
||||
},
|
||||
}
|
||||
|
||||
// Create a sweep request.
|
||||
sweepReq := SweepRequest{
|
||||
SwapHash: lntypes.Hash{1, 1, 1},
|
||||
Value: 111,
|
||||
SwapHash: swapHash,
|
||||
Value: amt,
|
||||
Outpoint: wire.OutPoint{
|
||||
Hash: chainhash.Hash{1, 1},
|
||||
Index: 1,
|
||||
|
|
@ -1770,23 +1815,13 @@ func testSweepFetcher(t *testing.T, store testStore,
|
|||
// Create a swap in the DB. It is needed to satisfy SQL constraints in
|
||||
// case of SQL test. The data is not actually used, since we pass sweep
|
||||
// fetcher, so put different conf target to make sure it is not used.
|
||||
swap := &loopdb.LoopOutContract{
|
||||
SwapContract: loopdb.SwapContract{
|
||||
CltvExpiry: 222,
|
||||
AmountRequested: 222,
|
||||
},
|
||||
|
||||
DestAddr: destAddr,
|
||||
SwapInvoice: swapInvoice,
|
||||
SweepConfTarget: 321,
|
||||
}
|
||||
err = store.CreateLoopOut(ctx, sweepReq.SwapHash, swap)
|
||||
err = store.CreateLoopOut(ctx, swapHash, swap)
|
||||
require.NoError(t, err)
|
||||
store.AssertLoopOutStored()
|
||||
|
||||
batcher := NewBatcher(lnd.WalletKit, lnd.ChainNotifier, lnd.Signer,
|
||||
testMuSig2SignSweep, nil, lnd.ChainParams, batcherStore,
|
||||
sweepFetcher)
|
||||
testMuSig2SignSweep, testVerifySchnorrSig, lnd.ChainParams,
|
||||
batcherStore, sweepFetcher)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
|
|
@ -1811,7 +1846,7 @@ func testSweepFetcher(t *testing.T, store testStore,
|
|||
// batch.
|
||||
require.Eventually(t, func() bool {
|
||||
// Make sure that the sweep was stored
|
||||
if !batcherStore.AssertSweepStored(sweepReq.SwapHash) {
|
||||
if !batcherStore.AssertSweepStored(swapHash) {
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
@ -1832,6 +1867,12 @@ func testSweepFetcher(t *testing.T, store testStore,
|
|||
return batch.cfg.batchConfTarget == 123
|
||||
}, test.Timeout, eventuallyCheckFrequency)
|
||||
|
||||
// Get the published transaction and check the fee rate.
|
||||
tx := <-lnd.TxPublishChannel
|
||||
out := btcutil.Amount(tx.TxOut[0].Value)
|
||||
gotFee := amt - out
|
||||
require.Equal(t, expectedFee, gotFee, "fees don't match")
|
||||
|
||||
// Make sure we have stored the batch.
|
||||
batches, err := batcherStore.FetchUnconfirmedSweepBatches(ctx)
|
||||
require.NoError(t, err)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue