Merge pull request #838 from starius/loopout-retarget-feerate-ever-block

loopout: re-target sweep's feerate every block
This commit is contained in:
Boris Nagaev 2024-11-11 20:56:35 -03:00 committed by GitHub
commit 0fe952a5c7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 600 additions and 40 deletions

View file

@ -62,8 +62,14 @@ var (
// probeTimeout is the maximum time until a probe is allowed to take.
probeTimeout = 3 * time.Minute
// repushDelay is the delay of (re)adding a sweep to sweepbatcher after
// a block is mined.
repushDelay = 1 * time.Second
// additionalDelay is the delay added on top of repushDelay inside the
// sweepbatcher to publish a sweep transaction.
additionalDelay = 1 * time.Second
// MinerFeeEstimationFailed is a magic number that is returned in a
// quote call as the miner fee if the fee estimation in lnd's wallet
// failed because of insufficient funds.
@ -185,13 +191,52 @@ func NewClient(dbDir string, loopDB loopdb.SwapStore,
"NewSweepFetcherFromSwapStore failed: %w", err)
}
// There is circular dependency between executor and sweepbatcher, as
// executor stores sweepbatcher and sweepbatcher depends on
// executor.height() though loopOutSweepFeerateProvider.
var executor *executor
// getHeight returns current height, according to executor.
getHeight := func() int32 {
if executor == nil {
// This must not happen, because executor is set in this
// function, before sweepbatcher.Run is called.
log.Errorf("getHeight called when executor is nil")
return 0
}
return executor.height()
}
loopOutSweepFeerateProvider := newLoopOutSweepFeerateProvider(
sweeper, loopDB, cfg.Lnd.ChainParams, getHeight,
)
batcher := sweepbatcher.NewBatcher(
cfg.Lnd.WalletKit, cfg.Lnd.ChainNotifier, cfg.Lnd.Signer,
swapServerClient.MultiMuSig2SignSweep, verifySchnorrSig,
cfg.Lnd.ChainParams, sweeperDb, sweepStore,
// Disable 100 sats/kw fee bump every block and retarget feerate
// every block according to the current mempool condition.
sweepbatcher.WithCustomFeeRate(
loopOutSweepFeerateProvider.GetMinFeeRate,
),
// Upon new block arrival, republishing is triggered in both
// loopout.go code (waitForHtlcSpendConfirmedV2/ <-timerChan)
// and in sweepbatcher code (batch.Run/case <-timerChan). The
// former updates the fee rate which is used by the later by
// calling AddSweep. Make sure they are ordered, add additional
// delay time to sweepbatcher's handling. The delay used in
// loopout.go is repushDelay.
sweepbatcher.WithPublishDelay(
repushDelay+additionalDelay,
),
)
executor := newExecutor(&executorConfig{
executor = newExecutor(&executorConfig{
lnd: cfg.Lnd,
store: loopDB,
sweeper: sweeper,
@ -570,8 +615,11 @@ func (s *Client) getLoopOutSweepFee(ctx context.Context, confTarget int32) (
htlc = swap.QuoteHtlcP2WSH
}
label := "loopout-quote"
return s.sweeper.GetSweepFee(
ctx, htlc.AddSuccessToEstimator, p2wshAddress, confTarget,
label,
)
}

View file

@ -106,13 +106,13 @@ const (
// time we reach timeout. We set this to a high estimate so that we can
// account for worst-case fees, (1250 * 4 / 1000) = 50 sat/byte.
defaultLoopInSweepFee = chainfee.SatPerKWeight(1250)
)
var (
// defaultHtlcConfTarget is the default confirmation target we use for
// loop in swap htlcs, set to the same default at the client.
defaultHtlcConfTarget = loop.DefaultHtlcConfTarget
)
var (
// defaultBudget is the default autoloop budget we set. This budget will
// only be used for automatically dispatched swaps if autoloop is
// explicitly enabled, so we are happy to set a non-zero value here. The

View file

@ -11,6 +11,7 @@ import (
"github.com/lightninglabs/loop/liquidity"
"github.com/lightninglabs/loop/loopdb"
"github.com/lightninglabs/loop/notifications"
"github.com/lightninglabs/loop/sweep"
"github.com/lightninglabs/loop/sweepbatcher"
"github.com/lightningnetwork/lnd"
"github.com/lightningnetwork/lnd/build"
@ -52,6 +53,9 @@ func SetupLoggers(root *build.RotatingLogWriter, intercept signal.Interceptor) {
lnd.AddSubLogger(
root, notifications.Subsystem, intercept, notifications.UseLogger,
)
lnd.AddSubLogger(
root, sweep.Subsystem, intercept, sweep.UseLogger,
)
}
// genSubLogger creates a logger for a subsystem. We provide an instance of

View file

@ -1077,10 +1077,12 @@ func (s *loopInSwap) publishTimeoutTx(ctx context.Context,
}
}
label := fmt.Sprintf("loopin-timeout-%x", s.hash[:6])
// Calculate sweep tx fee.
fee, err := s.sweeper.GetSweepFee(
ctx, s.htlc.AddTimeoutToEstimator, s.timeoutAddr,
TimeoutTxConfTarget,
TimeoutTxConfTarget, label,
)
if err != nil {
return 0, err

View file

@ -312,11 +312,13 @@ func handleHtlcExpiry(t *testing.T, ctx *loopInTestContext, inSwap *loopInSwap,
// Expect timeout tx to be published.
timeoutTx := <-ctx.lnd.TxPublishChannel
label := fmt.Sprintf("loopin-timeout-%x", inSwap.hash[:6])
// We can just get our sweep fee as we would in the swap code because
// our estimate is static.
fee, err := inSwap.sweeper.GetSweepFee(
context.Background(), inSwap.htlc.AddTimeoutToEstimator,
inSwap.timeoutAddr, TimeoutTxConfTarget,
inSwap.timeoutAddr, TimeoutTxConfTarget, label,
)
require.NoError(t, err)
cost.Onchain += fee

View file

@ -38,27 +38,33 @@ const (
// We'll try to sweep with MuSig2 at most 10 times. If that fails we'll
// fail back to using standard scriptspend sweep.
maxMusigSweepRetries = 10
)
var (
// MinLoopOutPreimageRevealDelta configures the minimum number of
// remaining blocks before htlc expiry required to reveal preimage.
MinLoopOutPreimageRevealDelta int32 = 20
MinLoopOutPreimageRevealDelta = 20
// DefaultSweepConfTarget is the default confirmation target we'll use
// when sweeping on-chain HTLCs.
DefaultSweepConfTarget int32 = 9
DefaultSweepConfTarget = 9
// DefaultHtlcConfTarget is the default confirmation target we'll use
// for on-chain htlcs published by the swap client for Loop In.
DefaultHtlcConfTarget int32 = 6
DefaultHtlcConfTarget = 6
// DefaultSweepConfTargetDelta is the delta of blocks from a Loop Out
// swap's expiration height at which we begin to use the default sweep
// confirmation target.
//
// TODO(wilmer): tune?
DefaultSweepConfTargetDelta = DefaultSweepConfTarget * 2
// swap's expiration height at which we begin to cap the sweep
// confirmation target with urgentSweepConfTarget and multiply feerate
// by factor urgentSweepConfTargetFactor.
DefaultSweepConfTargetDelta = 10
// urgentSweepConfTarget is the confirmation target we'll use when the
// loop-out swap is about to expire (<= DefaultSweepConfTargetDelta
// blocks to expire).
urgentSweepConfTarget = 3
// urgentSweepConfTargetFactor is the factor we apply to feerate of
// loop-out sweep if it is about to expire.
urgentSweepConfTargetFactor = 1.1
)
// loopOutSwap contains all the in-memory state related to a pending loop out
@ -1169,12 +1175,12 @@ func (s *loopOutSwap) waitForHtlcSpendConfirmedV2(globalCtx context.Context,
timerChan = s.timerFactory(repushDelay)
case <-timerChan:
// sweepConfTarget will return false if the preimage is
// canSweep will return false if the preimage is
// not revealed yet but the conf target is closer than
// 20 blocks. In this case to be sure we won't attempt
// to sweep at all and we won't reveal the preimage
// either.
_, canSweep := s.sweepConfTarget()
canSweep := s.canSweep()
if !canSweep {
s.log.Infof("Aborting swap, timed " +
"out on-chain")
@ -1375,9 +1381,9 @@ func validateLoopOutContract(lnd *lndclient.LndServices, request *OutRequest,
return nil
}
// sweepConfTarget returns the confirmation target for the htlc sweep or false
// if we're too late.
func (s *loopOutSwap) sweepConfTarget() (int32, bool) {
// canSweep will return false if the preimage is not revealed yet but the conf
// target is closer than 20 blocks (i.e. it is too late to reveal the preimage).
func (s *loopOutSwap) canSweep() bool {
remainingBlocks := s.CltvExpiry - s.height
blocksToLastReveal := remainingBlocks - MinLoopOutPreimageRevealDelta
preimageRevealed := s.state == loopdb.StatePreimageRevealed
@ -1393,20 +1399,8 @@ func (s *loopOutSwap) sweepConfTarget() (int32, bool) {
s.height)
s.state = loopdb.StateFailTimeout
return 0, false
return false
}
// Calculate the transaction fee based on the confirmation target
// required to sweep the HTLC before the timeout. We'll use the
// confirmation target provided by the client unless we've come too
// close to the expiration height, in which case we'll use the default
// if it is better than what the client provided.
confTarget := s.SweepConfTarget
if remainingBlocks <= DefaultSweepConfTargetDelta &&
confTarget > DefaultSweepConfTarget {
confTarget = DefaultSweepConfTarget
}
return confTarget, true
return true
}

198
loopout_feerate.go Normal file
View file

@ -0,0 +1,198 @@
package loop
import (
"context"
"fmt"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/txscript"
"github.com/lightninglabs/loop/loopdb"
"github.com/lightninglabs/loop/swap"
"github.com/lightninglabs/loop/utils"
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
)
// sweeper provides fee, fee rate and weight by confTarget.
type sweeper interface {
// 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 also takes a label used for
// logging. It returns also the fee rate and transaction weight.
GetSweepFeeDetails(ctx context.Context,
addInputEstimate func(*input.TxWeightEstimator) error,
destAddr btcutil.Address, sweepConfTarget int32, label string) (
btcutil.Amount, chainfee.SatPerKWeight, lntypes.WeightUnit,
error)
}
// loopOutFetcher provides the loop out swap with the given hash.
type loopOutFetcher interface {
// FetchLoopOutSwap returns the loop out swap with the given hash.
FetchLoopOutSwap(ctx context.Context,
hash lntypes.Hash) (*loopdb.LoopOut, error)
}
// heightGetter returns current height known to the swap server.
type heightGetter func() int32
// loopOutSweepFeerateProvider provides sweepbatcher with the info about swap's
// current feerate for loop-out sweep.
type loopOutSweepFeerateProvider struct {
// sweeper provides fee, fee rate and weight by confTarget.
sweeper sweeper
// loopOutFetcher loads LoopOut from DB by swap hash.
loopOutFetcher loopOutFetcher
// chainParams are the chain parameters of the chain that is used by
// swaps.
chainParams *chaincfg.Params
// getHeight returns current height known to the swap server.
getHeight heightGetter
}
// newLoopOutSweepFeerateProvider builds and returns new instance of
// loopOutSweepFeerateProvider.
func newLoopOutSweepFeerateProvider(sweeper sweeper,
loopOutFetcher loopOutFetcher, chainParams *chaincfg.Params,
getHeight heightGetter) *loopOutSweepFeerateProvider {
return &loopOutSweepFeerateProvider{
sweeper: sweeper,
loopOutFetcher: loopOutFetcher,
chainParams: chainParams,
getHeight: getHeight,
}
}
// GetMinFeeRate returns minimum required feerate for a sweep by swap hash.
func (p *loopOutSweepFeerateProvider) GetMinFeeRate(ctx context.Context,
swapHash lntypes.Hash) (chainfee.SatPerKWeight, error) {
_, feeRate, err := p.GetConfTargetAndFeeRate(ctx, swapHash)
return feeRate, err
}
// GetConfTargetAndFeeRate returns conf target and minimum required feerate
// for a sweep by swap hash.
func (p *loopOutSweepFeerateProvider) GetConfTargetAndFeeRate(
ctx context.Context, swapHash lntypes.Hash) (int32,
chainfee.SatPerKWeight, error) {
// Load the loop-out from DB.
loopOut, err := p.loopOutFetcher.FetchLoopOutSwap(ctx, swapHash)
if err != nil {
return 0, 0, fmt.Errorf("failed to load swap %x from DB: %w",
swapHash[:6], err)
}
contract := loopOut.Contract
if contract == nil {
return 0, 0, fmt.Errorf("loop-out %x has nil Contract",
swapHash[:6])
}
// Determine if we can keyspend.
htlcVersion := utils.GetHtlcScriptVersion(contract.ProtocolVersion)
canKeyspend := htlcVersion >= swap.HtlcV3
// Find addInputToEstimator function.
var addInputToEstimator func(e *input.TxWeightEstimator) error
if canKeyspend {
// Assume the server is cooperative and we produce keyspend.
addInputToEstimator = func(e *input.TxWeightEstimator) error {
e.AddTaprootKeySpendInput(txscript.SigHashDefault)
return nil
}
} else {
// Get the HTLC script for our swap.
htlc, err := utils.GetHtlc(
swapHash, &contract.SwapContract, p.chainParams,
)
if err != nil {
return 0, 0, fmt.Errorf("failed to get HTLC: %w", err)
}
addInputToEstimator = htlc.AddSuccessToEstimator
}
// Transaction weight might be important for feeRate, in case of high
// priority proportional fee, so we accurately assess the size of input.
// The size of output is almost the same for all types, so use P2TR.
var destAddr *btcutil.AddressTaproot
// Get current height.
height := p.getHeight()
if height == 0 {
return 0, 0, fmt.Errorf("got zero best block height")
}
// blocksUntilExpiry is the number of blocks until the htlc timeout path
// opens for the client to sweep.
blocksUntilExpiry := contract.CltvExpiry - height
// Find confTarget. If the sweep has expired, use confTarget=1, because
// confTarget must be positive.
confTarget := blocksUntilExpiry
if confTarget <= 0 {
log.Infof("Swap %x has expired (blocksUntilExpiry=%d), using "+
"confTarget=1 for it.", swapHash[:6], blocksUntilExpiry)
confTarget = 1
}
feeFactor := float64(1.0)
// If confTarget is less than or equal to DefaultSweepConfTargetDelta,
// cap it with urgentSweepConfTarget and apply fee factor.
if confTarget <= DefaultSweepConfTargetDelta {
// If confTarget is already <= urgentSweepConfTarget, don't
// increase it.
newConfTarget := int32(urgentSweepConfTarget)
if confTarget < newConfTarget {
newConfTarget = confTarget
}
log.Infof("Swap %x is about to expire (blocksUntilExpiry=%d), "+
"reducing its confTarget from %d to %d and multiplying"+
" feerate by %v.", swapHash[:6], blocksUntilExpiry,
confTarget, newConfTarget, urgentSweepConfTargetFactor)
confTarget = newConfTarget
feeFactor = urgentSweepConfTargetFactor
}
// Construct the label.
label := fmt.Sprintf("loopout-sweep-%x", swapHash[:6])
// Estimate confTarget and feeRate.
_, feeRate, _, err := p.sweeper.GetSweepFeeDetails(
ctx, addInputToEstimator, destAddr, confTarget, label,
)
if err != nil {
return 0, 0, fmt.Errorf("fee estimator failed, swapHash=%x, "+
"confTarget=%d: %w", swapHash[:6], confTarget, err)
}
// Multiply feerate by fee factor.
feeRate = chainfee.SatPerKWeight(float64(feeRate) * feeFactor)
// Sanity check. Make sure fee rate is not too low.
const minFeeRate = chainfee.AbsoluteFeePerKwFloor
if feeRate < minFeeRate {
log.Infof("Got too low fee rate for swap %x: %v. Increasing "+
"it to %v.", swapHash[:6], feeRate, minFeeRate)
feeRate = minFeeRate
}
log.Debugf("Estimated for swap %x: feeRate=%s, confTarget=%d.",
swapHash[:6], feeRate, confTarget)
return confTarget, feeRate, nil
}

278
loopout_feerate_test.go Normal file
View file

@ -0,0 +1,278 @@
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:
feeRate = 30000
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: 1,
wantFeeRate: 33000,
},
{
name: "expired",
cltvExpiry: 801_000,
height: 801_000,
amount: 1_000_000,
protocolVersion: loopdb.ProtocolVersionMuSig2,
wantConfTarget: 1,
wantFeeRate: 33000,
},
}
for _, tc := range cases {
tc := tc
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)
})
}
}

26
sweep/log.go Normal file
View file

@ -0,0 +1,26 @@
package sweep
import (
"github.com/btcsuite/btclog"
"github.com/lightningnetwork/lnd/build"
)
// Subsystem defines the sub system name of this package.
const Subsystem = "SWP"
// log is a logger that is initialized with no output filters. This means the
// package will not perform any logging by default until the caller requests
// it.
var log btclog.Logger
// The default amount of logging is none.
func init() {
UseLogger(build.NewSubLogger(Subsystem, nil))
}
// UseLogger uses a specified Logger to output package logging info. This
// should be used in preference to SetLogWriter if the caller is also using
// btclog.
func UseLogger(logger btclog.Logger) {
log = logger
}

View file

@ -177,15 +177,15 @@ func (s *Sweeper) CreateSweepTx(
// GetSweepFee calculates the required tx fee to spend to P2WKH. It takes a
// function that is expected to add the weight of the input to the weight
// estimator.
// estimator. It also takes a label used for logging.
func (s *Sweeper) GetSweepFee(ctx context.Context,
addInputEstimate func(*input.TxWeightEstimator) error,
destAddr btcutil.Address, sweepConfTarget int32) (
destAddr btcutil.Address, sweepConfTarget int32, label string) (
btcutil.Amount, error) {
// Use GetSweepFeeDetails to get the fee and other unused data.
fee, _, _, err := s.GetSweepFeeDetails(
ctx, addInputEstimate, destAddr, sweepConfTarget,
ctx, addInputEstimate, destAddr, sweepConfTarget, label,
)
return fee, err
@ -193,10 +193,11 @@ func (s *Sweeper) GetSweepFee(ctx context.Context,
// GetSweepFeeDetails calculates the required tx fee to spend to P2WKH. 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.
// estimator. It also takes a label used for logging. It returns also the fee
// rate and transaction weight.
func (s *Sweeper) GetSweepFeeDetails(ctx context.Context,
addInputEstimate func(*input.TxWeightEstimator) error,
destAddr btcutil.Address, sweepConfTarget int32) (
destAddr btcutil.Address, sweepConfTarget int32, label string) (
btcutil.Amount, chainfee.SatPerKWeight, lntypes.WeightUnit, error) {
// Get fee estimate from lnd.
@ -224,7 +225,14 @@ func (s *Sweeper) GetSweepFeeDetails(ctx context.Context,
// Find weight.
weight := weightEstimate.Weight()
return feeRate.FeeForWeight(weight), feeRate, weight, nil
// Find fee.
fee := feeRate.FeeForWeight(weight)
log.Debugf("Estimations for a tx (label=%s): weight=%v, fee=%v, "+
"feerate=%v, sweepConfTarget=%d.", label, weight, fee, feeRate,
sweepConfTarget)
return fee, feeRate, weight, nil
}
// AddOutputEstimate adds output to weight estimator.