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.
266 lines
7.9 KiB
Go
266 lines
7.9 KiB
Go
package loopd
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"slices"
|
|
|
|
"github.com/btcsuite/btcd/btcutil/v2"
|
|
"github.com/btcsuite/btcd/chaincfg/v2"
|
|
"github.com/lightninglabs/aperture/l402"
|
|
"github.com/lightninglabs/lndclient"
|
|
"github.com/lightninglabs/loop"
|
|
"github.com/lightninglabs/loop/assets"
|
|
"github.com/lightninglabs/loop/liquidity"
|
|
"github.com/lightninglabs/loop/loopdb"
|
|
"github.com/lightninglabs/loop/staticaddr/loopin"
|
|
"github.com/lightninglabs/loop/swap"
|
|
"github.com/lightninglabs/loop/sweepbatcher"
|
|
"github.com/lightningnetwork/lnd/clock"
|
|
"github.com/lightningnetwork/lnd/routing/route"
|
|
"github.com/lightningnetwork/lnd/ticker"
|
|
)
|
|
|
|
// getClient returns an instance of the swap client.
|
|
func getClient(cfg *Config, swapDb loopdb.SwapStore,
|
|
sweeperDb sweepbatcher.BatcherStore, lnd *lndclient.LndServices,
|
|
assets *assets.TapdClient) (*loop.Client, func(), error) {
|
|
|
|
// Default is not set for MaxLSATCost and MaxLSATFee to distinguish
|
|
// it from user explicitly setting the option to default value.
|
|
// So if MaxL402Cost and MaxLSATFee are not set in the config file
|
|
// and command line, they are set to 0.
|
|
const (
|
|
defaultCost = l402.DefaultMaxCostSats
|
|
defaultFee = l402.DefaultMaxRoutingFeeSats
|
|
)
|
|
if cfg.MaxL402Cost != defaultCost && cfg.MaxLSATCost != 0 {
|
|
return nil, nil, fmt.Errorf("both maxl402cost and maxlsatcost" +
|
|
" were specified; they are not allowed together")
|
|
}
|
|
if cfg.MaxL402Fee != defaultFee && cfg.MaxLSATFee != 0 {
|
|
return nil, nil, fmt.Errorf("both maxl402fee and maxlsatfee" +
|
|
" were specified; they are not allowed together")
|
|
}
|
|
|
|
clientConfig := &loop.ClientConfig{
|
|
ServerAddress: cfg.Server.Host,
|
|
ProxyAddress: cfg.Server.Proxy,
|
|
SwapServerNoTLS: cfg.Server.NoTLS,
|
|
TLSPathServer: cfg.Server.TLSPath,
|
|
Lnd: lnd,
|
|
AssetClient: assets,
|
|
MaxL402Cost: btcutil.Amount(cfg.MaxL402Cost),
|
|
MaxL402Fee: btcutil.Amount(cfg.MaxL402Fee),
|
|
LoopOutMaxParts: cfg.LoopOutMaxParts,
|
|
SkippedTxns: cfg.SkippedTxns,
|
|
TotalPaymentTimeout: cfg.TotalPaymentTimeout,
|
|
MaxPaymentRetries: cfg.MaxPaymentRetries,
|
|
MaxStaticAddrHtlcFeePercentage: cfg.MaxStaticAddrHtlcFeePercentage,
|
|
MaxStaticAddrHtlcBackupFeePercentage: cfg.MaxStaticAddrHtlcBackupFeePercentage,
|
|
}
|
|
|
|
if cfg.MaxL402Cost == defaultCost && cfg.MaxLSATCost != 0 {
|
|
warnf("Option maxlsatcost is deprecated and will be " +
|
|
"removed. Switch to maxl402cost.")
|
|
clientConfig.MaxL402Cost = btcutil.Amount(cfg.MaxLSATCost)
|
|
}
|
|
if cfg.MaxL402Fee == defaultFee && cfg.MaxLSATFee != 0 {
|
|
warnf("Option maxlsatfee is deprecated and will be " +
|
|
"removed. Switch to maxl402fee.")
|
|
clientConfig.MaxL402Fee = btcutil.Amount(cfg.MaxLSATFee)
|
|
}
|
|
|
|
swapClient, cleanUp, err := loop.NewClient(
|
|
cfg.DataDir, swapDb, sweeperDb, clientConfig,
|
|
)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
return swapClient, cleanUp, nil
|
|
}
|
|
|
|
func openDatabase(cfg *Config, chainParams *chaincfg.Params) (loopdb.SwapStore,
|
|
*loopdb.BaseDB, error) { //nolint:unparam
|
|
|
|
var (
|
|
db loopdb.SwapStore
|
|
err error
|
|
baseDb loopdb.BaseDB
|
|
)
|
|
switch cfg.DatabaseBackend {
|
|
case DatabaseBackendSqlite:
|
|
infof("Opening sqlite3 database at: %v",
|
|
cfg.Sqlite.DatabaseFileName)
|
|
|
|
db, err = loopdb.NewSqliteStore(cfg.Sqlite, chainParams)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
baseDb = *db.(*loopdb.SqliteSwapStore).BaseDB
|
|
|
|
case DatabaseBackendPostgres:
|
|
infof("Opening postgres database at: %v",
|
|
cfg.Postgres.DSN(true))
|
|
|
|
db, err = loopdb.NewPostgresStore(cfg.Postgres, chainParams)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
baseDb = *db.(*loopdb.PostgresStore).BaseDB
|
|
|
|
default:
|
|
return nil, nil, fmt.Errorf("unknown database backend: %s",
|
|
cfg.DatabaseBackend)
|
|
}
|
|
|
|
return db, &baseDb, nil
|
|
}
|
|
|
|
func getLiquidityManager(client *loop.Client,
|
|
staticLoopInManager *loopin.Manager,
|
|
enableExperimental bool) *liquidity.Manager {
|
|
|
|
listStaticLoopIn := func(
|
|
ctx context.Context) ([]*liquidity.StaticLoopInInfo, error) {
|
|
|
|
if staticLoopInManager == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
swaps, err := staticLoopInManager.GetAllSwaps(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result := make(
|
|
[]*liquidity.StaticLoopInInfo, 0, len(swaps),
|
|
)
|
|
for _, staticSwap := range swaps {
|
|
state := staticSwap.GetState()
|
|
pending := slices.Contains(loopin.PendingStates, state)
|
|
failed := state == loopin.Failed ||
|
|
state == loopin.HtlcTimeoutSwept
|
|
|
|
result = append(result, &liquidity.StaticLoopInInfo{
|
|
Label: staticSwap.Label,
|
|
QuotedSwapFee: staticSwap.QuotedSwapFee,
|
|
HtlcTxFeeRate: staticSwap.HtlcTxFeeRate,
|
|
LastHop: staticSwap.LastHopVertex(),
|
|
LastUpdateTime: staticSwap.LastUpdateTime,
|
|
Pending: pending,
|
|
Failed: failed,
|
|
BlocksLoopIn: pending &&
|
|
state != loopin.PaymentReceived,
|
|
NumDeposits: len(staticSwap.Deposits),
|
|
HasChange: staticSwap.SelectedAmount > 0 &&
|
|
staticSwap.SelectedAmount <
|
|
staticSwap.TotalDepositAmount(),
|
|
})
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
prepareStaticLoopIn := func(ctx context.Context, peer route.Vertex,
|
|
minAmount, amount btcutil.Amount, label, initiator string,
|
|
excludedOutpoints []string) (*liquidity.PreparedStaticLoopIn,
|
|
error) {
|
|
|
|
if staticLoopInManager == nil {
|
|
return nil, errors.New(
|
|
"static loop in manager unavailable",
|
|
)
|
|
}
|
|
|
|
request, numDeposits, hasChange, err :=
|
|
staticLoopInManager.PrepareAutoloopLoopIn(
|
|
ctx, peer, minAmount, amount, label,
|
|
initiator, excludedOutpoints,
|
|
)
|
|
if errors.Is(err, loopin.ErrNoAutoloopCandidate) {
|
|
return nil, liquidity.ErrNoStaticLoopInCandidate
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &liquidity.PreparedStaticLoopIn{
|
|
Request: *request,
|
|
NumDeposits: numDeposits,
|
|
HasChange: hasChange,
|
|
}, nil
|
|
}
|
|
|
|
staticLoopIn := func(ctx context.Context,
|
|
request *loop.StaticAddressLoopInRequest) (
|
|
*liquidity.StaticLoopInDispatchResult, error) {
|
|
|
|
if staticLoopInManager == nil {
|
|
return nil, errors.New(
|
|
"static loop in manager unavailable",
|
|
)
|
|
}
|
|
|
|
swapInfo, err := staticLoopInManager.DeliverLoopInRequest(
|
|
ctx, request,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &liquidity.StaticLoopInDispatchResult{
|
|
SwapHash: swapInfo.SwapHash,
|
|
}, nil
|
|
}
|
|
|
|
mngrCfg := &liquidity.Config{
|
|
AutoloopTicker: ticker.NewForce(liquidity.DefaultAutoloopTicker),
|
|
LoopOut: client.LoopOut,
|
|
LoopIn: client.LoopIn,
|
|
Restrictions: func(ctx context.Context, swapType swap.Type,
|
|
initiator string) (*liquidity.Restrictions, error) {
|
|
|
|
if swapType == swap.TypeOut {
|
|
outTerms, err := client.Server.GetLoopOutTerms(ctx, initiator)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return liquidity.NewRestrictions(
|
|
outTerms.MinSwapAmount, outTerms.MaxSwapAmount,
|
|
), nil
|
|
}
|
|
|
|
inTerms, err := client.Server.GetLoopInTerms(ctx, initiator)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return liquidity.NewRestrictions(
|
|
inTerms.MinSwapAmount, inTerms.MaxSwapAmount,
|
|
), nil
|
|
},
|
|
Lnd: client.LndServices,
|
|
Clock: clock.NewDefaultClock(),
|
|
LoopOutQuote: client.LoopOutQuote,
|
|
LoopInQuote: client.LoopInQuote,
|
|
ListLoopOut: client.Store.FetchLoopOutSwaps,
|
|
GetLoopOut: client.Store.FetchLoopOutSwap,
|
|
ListLoopIn: client.Store.FetchLoopInSwaps,
|
|
ListStaticLoopIn: listStaticLoopIn,
|
|
PrepareStaticLoopIn: prepareStaticLoopIn,
|
|
StaticLoopIn: staticLoopIn,
|
|
LoopInTerms: client.LoopInTerms,
|
|
LoopOutTerms: client.LoopOutTerms,
|
|
GetAssetPrice: client.AssetClient.GetAssetPrice,
|
|
MinimumConfirmations: minConfTarget,
|
|
PutLiquidityParams: client.Store.PutLiquidityParams,
|
|
FetchLiquidityParams: client.Store.FetchLiquidityParams,
|
|
}
|
|
mngrCfg.EnableStaticAddressAutoloop = enableExperimental
|
|
|
|
return liquidity.NewManager(mngrCfg)
|
|
}
|