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.
pull/785/head
Boris Nagaev 3 months ago
parent 4e085f11a3
commit fd971d4951
No known key found for this signature in database

@ -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…
Cancel
Save