loop/loopout_feerate_test.go

278 lines
7.2 KiB
Go
Raw Permalink Normal View History

package loop
import (
"context"
"fmt"
"testing"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg"
"github.com/lightninglabs/loop/loopdb"
"github.com/lightninglabs/loop/sweep"
"github.com/lightninglabs/loop/test"
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/stretchr/testify/require"
)
// testSweeper is implementation of sweeper.Sweeper for test.
type testSweeper struct {
}
// GetSweepFeeDetails calculates the required tx fee to spend to destAddr. It
// takes a function that is expected to add the weight of the input to the
// weight estimator. It returns also the fee rate and transaction weight.
func (s testSweeper) GetSweepFeeDetails(ctx context.Context,
addInputEstimate func(*input.TxWeightEstimator) error,
destAddr btcutil.Address, sweepConfTarget int32,
label string) (btcutil.Amount, chainfee.SatPerKWeight,
lntypes.WeightUnit, error) {
var feeRate chainfee.SatPerKWeight
switch {
case sweepConfTarget == 0:
return 0, 0, 0, fmt.Errorf("zero sweepConfTarget")
case sweepConfTarget == 1:
return 0, 0, 0, fmt.Errorf("sweepConfTarget is 1")
case sweepConfTarget == 2:
feeRate = 25000
case sweepConfTarget == 3:
feeRate = 20000
case sweepConfTarget < 10:
feeRate = 8000
case sweepConfTarget < 100:
feeRate = 5000
case sweepConfTarget < 1000:
feeRate = 2000
default:
feeRate = 250
}
// Calculate weight for this tx.
var weightEstimate input.TxWeightEstimator
// Add output.
err := sweep.AddOutputEstimate(&weightEstimate, destAddr)
if err != nil {
return 0, 0, 0, fmt.Errorf("failed to add output weight "+
"estimate: %w", err)
}
// Add input.
err = addInputEstimate(&weightEstimate)
if err != nil {
return 0, 0, 0, fmt.Errorf("failed to add input weight "+
"estimate: %w", err)
}
// Find weight.
weight := weightEstimate.Weight()
return feeRate.FeeForWeight(weight), feeRate, weight, nil
}
// TestLoopOutSweepFeerateProvider tests that loopOutSweepFeerateProvider
// provides correct fee rate for loop-out swaps.
func TestLoopOutSweepFeerateProvider(t *testing.T) {
htlcKeys := func() loopdb.HtlcKeys {
var senderKey, receiverKey [33]byte
// Generate keys.
_, senderPubKey := test.CreateKey(1)
copy(senderKey[:], senderPubKey.SerializeCompressed())
_, receiverPubKey := test.CreateKey(2)
copy(receiverKey[:], receiverPubKey.SerializeCompressed())
return loopdb.HtlcKeys{
SenderScriptKey: senderKey,
ReceiverScriptKey: receiverKey,
SenderInternalPubKey: senderKey,
ReceiverInternalPubKey: receiverKey,
}
}()
var destAddr *btcutil.AddressTaproot
swapInvoice := "lntb1230n1pjjszzgpp5j76f03wrkya4sm4gxv6az5nmz5aqsvmn4" +
"tpguu2sdvdyygedqjgqdq9xyerxcqzzsxqr23ssp5rwzmwtfjmsgranfk8sr" +
"4p4gcgmvyd42uug8pxteg2mkk23ndvkqs9qyyssq44ruk3ex59cmv4dm6k4v" +
"0kc6c0gcqjs0gkljfyd6c6uatqa2f67xlx3pcg5tnvcae5p3jju8ra77e87d" +
"vhhs0jrx53wnc0fq9rkrhmqqelyx7l"
cases := []struct {
name string
cltvExpiry int32
height int32
amount btcutil.Amount
protocolVersion loopdb.ProtocolVersion
wantConfTarget int32
wantFeeRate chainfee.SatPerKWeight
wantError string
}{
{
name: "simple case",
cltvExpiry: 801_000,
height: 800_900,
amount: 1_000_000,
protocolVersion: loopdb.ProtocolVersionMuSig2,
wantConfTarget: 100,
wantFeeRate: 2000,
},
{
name: "zero height",
cltvExpiry: 801_000,
height: 0,
amount: 1_000_000,
protocolVersion: loopdb.ProtocolVersionMuSig2,
wantError: "got zero best block height",
},
{
name: "huge amount, no proportional fee",
cltvExpiry: 801_000,
height: 800_900,
amount: 100_000_000,
protocolVersion: loopdb.ProtocolVersionMuSig2,
wantConfTarget: 100,
wantFeeRate: 2000,
},
{
name: "huge amount, no proportional fee, v2",
cltvExpiry: 801_000,
height: 800_900,
amount: 100_000_000,
protocolVersion: loopdb.ProtocolVersionLoopOutCancel,
wantConfTarget: 100,
wantFeeRate: 2000,
},
{
name: "huge amount, no proportional fee, " +
"capped by urgent fee",
cltvExpiry: 801_000,
height: 800_900,
amount: 200_000_000,
protocolVersion: loopdb.ProtocolVersionMuSig2,
wantConfTarget: 100,
wantFeeRate: 2000,
},
{
name: "11 blocks until expiry",
cltvExpiry: 801_000,
height: 800_989,
amount: 1_000_000,
protocolVersion: loopdb.ProtocolVersionMuSig2,
wantConfTarget: 11,
wantFeeRate: 5000,
},
{
name: "10 blocks until expiry",
cltvExpiry: 801_000,
height: 800_990,
amount: 1_000_000,
protocolVersion: loopdb.ProtocolVersionMuSig2,
wantConfTarget: 3,
wantFeeRate: 22000,
},
{
name: "9 blocks until expiry",
cltvExpiry: 801_000,
height: 800_991,
amount: 1_000_000,
protocolVersion: loopdb.ProtocolVersionMuSig2,
wantConfTarget: 3,
wantFeeRate: 22000,
},
{
name: "3 blocks until expiry",
cltvExpiry: 801_000,
height: 800_997,
amount: 1_000_000,
protocolVersion: loopdb.ProtocolVersionMuSig2,
wantConfTarget: 3,
wantFeeRate: 22000,
},
{
name: "2 blocks until expiry",
cltvExpiry: 801_000,
height: 800_998,
amount: 1_000_000,
protocolVersion: loopdb.ProtocolVersionMuSig2,
wantConfTarget: 2,
wantFeeRate: 27500,
},
{
name: "1 blocks until expiry",
cltvExpiry: 801_000,
height: 800_999,
amount: 1_000_000,
protocolVersion: loopdb.ProtocolVersionMuSig2,
wantConfTarget: 2,
wantFeeRate: 27500,
},
{
name: "expired",
cltvExpiry: 801_000,
height: 801_000,
amount: 1_000_000,
protocolVersion: loopdb.ProtocolVersionMuSig2,
wantConfTarget: 2,
wantFeeRate: 27500,
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
store := loopdb.NewStoreMock(t)
ctx := context.Background()
swapHash := lntypes.Hash{1, 1, 1}
swap := &loopdb.LoopOutContract{
SwapContract: loopdb.SwapContract{
CltvExpiry: tc.cltvExpiry,
AmountRequested: tc.amount,
ProtocolVersion: tc.protocolVersion,
HtlcKeys: htlcKeys,
},
DestAddr: destAddr,
SwapInvoice: swapInvoice,
SweepConfTarget: 100,
}
err := store.CreateLoopOut(ctx, swapHash, swap)
require.NoError(t, err)
store.AssertLoopOutStored()
getHeight := func() int32 {
return tc.height
}
p := newLoopOutSweepFeerateProvider(
testSweeper{}, store,
&chaincfg.RegressionNetParams, getHeight,
)
confTarget, feeRate, err := p.GetConfTargetAndFeeRate(
ctx, swapHash,
)
if tc.wantError != "" {
require.ErrorContains(t, err, tc.wantError)
return
}
require.NoError(t, err)
require.Equal(t, tc.wantConfTarget, confTarget)
require.Equal(t, tc.wantFeeRate, feeRate)
})
}
}