mirror of
https://github.com/lightninglabs/loop.git
synced 2026-08-13 12:33:03 +02:00
Update LND, Aperture, and Taproot Assets to revisions using the btcd v2 modules, and update lndclient to v0.21.0-3. Migrate Loop chain, transaction, and address types to their corresponding v2 packages. The lndclient release includes the migration from: https://github.com/lightninglabs/lndclient/pull/280 Taproot Assets is temporarily replaced with its btcd v2 revision because the v0.8 release branch has not adopted the new modules. This raises the minimum Go version to 1.26 and changes exported address types.
198 lines
6.3 KiB
Go
198 lines
6.3 KiB
Go
package loop
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
btcaddr "github.com/btcsuite/btcd/address/v2"
|
|
"github.com/btcsuite/btcd/btcutil/v2"
|
|
"github.com/btcsuite/btcd/chaincfg/v2"
|
|
"github.com/btcsuite/btcd/txscript/v2"
|
|
"github.com/btcsuite/btcd/wire/v2"
|
|
"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 btcaddr.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,
|
|
_ wire.OutPoint) (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 *btcaddr.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=2, because
|
|
// fee estimator fails if confTarget < 2.
|
|
confTarget := blocksUntilExpiry
|
|
if confTarget < 2 {
|
|
log.Infof("Swap %x has expired (blocksUntilExpiry=%d), using "+
|
|
"confTarget=2 for it.", swapHash[:6], blocksUntilExpiry)
|
|
|
|
confTarget = 2
|
|
}
|
|
|
|
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 := min(confTarget, int32(urgentSweepConfTarget))
|
|
|
|
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
|
|
}
|