mirror of
https://github.com/lightninglabs/loop.git
synced 2026-08-13 12:33:03 +02:00
Include https://github.com/lightninglabs/lndclient/pull/280 multi: migrate to btcd v2 modules + add WalletKit.SubmitPackage Migrate Loop imports and update Aperture and Taproot Assets to compatible revisions so this commit remains green on its own.
278 lines
7.2 KiB
Go
278 lines
7.2 KiB
Go
package loop
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"testing"
|
|
|
|
btcaddr "github.com/btcsuite/btcd/address/v2"
|
|
"github.com/btcsuite/btcd/btcutil/v2"
|
|
"github.com/btcsuite/btcd/chaincfg/v2"
|
|
"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 btcaddr.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 *btcaddr.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)
|
|
})
|
|
}
|
|
}
|