Merge pull request #872 from sputn1ck/asset_simple_loopout

Simple Asset Loop out
This commit is contained in:
Konstantin Nick 2025-01-21 19:42:11 +01:00 committed by GitHub
commit 684db85a95
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
34 changed files with 2786 additions and 1154 deletions

218
assets/client.go Normal file
View file

@ -0,0 +1,218 @@
package assets
import (
"context"
"encoding/hex"
"fmt"
"os"
"sync"
"time"
"github.com/btcsuite/btcd/btcutil"
"github.com/lightninglabs/taproot-assets/tapcfg"
"github.com/lightninglabs/taproot-assets/taprpc"
"github.com/lightninglabs/taproot-assets/taprpc/priceoraclerpc"
"github.com/lightninglabs/taproot-assets/taprpc/rfqrpc"
"github.com/lightninglabs/taproot-assets/taprpc/tapchannelrpc"
"github.com/lightninglabs/taproot-assets/taprpc/universerpc"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/macaroons"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"gopkg.in/macaroon.v2"
)
var (
// maxMsgRecvSize is the largest message our client will receive. We
// set this to 200MiB atm.
maxMsgRecvSize = grpc.MaxCallRecvMsgSize(1 * 1024 * 1024 * 200)
// defaultRfqTimeout is the default timeout we wait for tapd peer to
// accept RFQ.
defaultRfqTimeout = time.Second * 60
)
// TapdConfig is a struct that holds the configuration options to connect to a
// taproot assets daemon.
type TapdConfig struct {
Activate bool `long:"activate" description:"Activate the Tap daemon"`
Host string `long:"host" description:"The host of the Tap daemon, in the format of host:port"`
MacaroonPath string `long:"macaroonpath" description:"Path to the admin macaroon"`
TLSPath string `long:"tlspath" description:"Path to the TLS certificate"`
RFQtimeout time.Duration `long:"rfqtimeout" description:"The timeout we wait for tapd peer to accept RFQ"`
}
// DefaultTapdConfig returns a default configuration to connect to a taproot
// assets daemon.
func DefaultTapdConfig() *TapdConfig {
defaultConf := tapcfg.DefaultConfig()
return &TapdConfig{
Activate: false,
Host: "localhost:10029",
MacaroonPath: defaultConf.RpcConf.MacaroonPath,
TLSPath: defaultConf.RpcConf.TLSCertPath,
RFQtimeout: defaultRfqTimeout,
}
}
// TapdClient is a client for the Tap daemon.
type TapdClient struct {
taprpc.TaprootAssetsClient
tapchannelrpc.TaprootAssetChannelsClient
priceoraclerpc.PriceOracleClient
rfqrpc.RfqClient
universerpc.UniverseClient
cfg *TapdConfig
assetNameCache map[string]string
assetNameMutex sync.Mutex
cc *grpc.ClientConn
}
// NewTapdClient returns a new taproot assets client.
func NewTapdClient(config *TapdConfig) (*TapdClient, error) {
// Create the client connection to the server.
conn, err := getClientConn(config)
if err != nil {
return nil, err
}
// Create the TapdClient.
client := &TapdClient{
assetNameCache: make(map[string]string),
cc: conn,
cfg: config,
TaprootAssetsClient: taprpc.NewTaprootAssetsClient(conn),
TaprootAssetChannelsClient: tapchannelrpc.NewTaprootAssetChannelsClient(conn),
PriceOracleClient: priceoraclerpc.NewPriceOracleClient(conn),
RfqClient: rfqrpc.NewRfqClient(conn),
UniverseClient: universerpc.NewUniverseClient(conn),
}
return client, nil
}
// Close closes the client connection to the server.
func (c *TapdClient) Close() {
c.cc.Close()
}
// GetRfqForAsset returns a RFQ for the given asset with the given amount and
// to the given peer.
func (c *TapdClient) GetRfqForAsset(ctx context.Context,
satAmount btcutil.Amount, assetId, peerPubkey []byte,
expiry int64, feeLimitMultiplier float64) (
*rfqrpc.PeerAcceptedSellQuote, error) {
feeLimit, err := lnrpc.UnmarshallAmt(
int64(satAmount)+int64(satAmount.MulF64(feeLimitMultiplier)), 0,
)
if err != nil {
return nil, err
}
rfq, err := c.RfqClient.AddAssetSellOrder(
ctx, &rfqrpc.AddAssetSellOrderRequest{
AssetSpecifier: &rfqrpc.AssetSpecifier{
Id: &rfqrpc.AssetSpecifier_AssetId{
AssetId: assetId,
},
},
PeerPubKey: peerPubkey,
PaymentMaxAmt: uint64(feeLimit),
Expiry: uint64(expiry),
TimeoutSeconds: uint32(c.cfg.RFQtimeout.Seconds()),
})
if err != nil {
return nil, err
}
if rfq.GetInvalidQuote() != nil {
return nil, fmt.Errorf("invalid RFQ: %v", rfq.GetInvalidQuote())
}
if rfq.GetRejectedQuote() != nil {
return nil, fmt.Errorf("rejected RFQ: %v",
rfq.GetRejectedQuote())
}
if rfq.GetAcceptedQuote() != nil {
return rfq.GetAcceptedQuote(), nil
}
return nil, fmt.Errorf("no accepted quote")
}
// GetAssetName returns the human-readable name of the asset.
func (c *TapdClient) GetAssetName(ctx context.Context,
assetId []byte) (string, error) {
c.assetNameMutex.Lock()
defer c.assetNameMutex.Unlock()
assetIdStr := hex.EncodeToString(assetId)
if name, ok := c.assetNameCache[assetIdStr]; ok {
return name, nil
}
assetStats, err := c.UniverseClient.QueryAssetStats(
ctx, &universerpc.AssetStatsQuery{
AssetIdFilter: assetId,
},
)
if err != nil {
return "", err
}
if len(assetStats.AssetStats) == 0 {
return "", fmt.Errorf("asset not found")
}
var assetName string
// If the asset belongs to a group, return the group name.
if assetStats.AssetStats[0].GroupAnchor != nil {
assetName = assetStats.AssetStats[0].GroupAnchor.AssetName
} else {
assetName = assetStats.AssetStats[0].Asset.AssetName
}
c.assetNameCache[assetIdStr] = assetName
return assetName, nil
}
func getClientConn(config *TapdConfig) (*grpc.ClientConn, error) {
// Load the specified TLS certificate and build transport credentials.
creds, err := credentials.NewClientTLSFromFile(config.TLSPath, "")
if err != nil {
return nil, err
}
// Load the specified macaroon file.
macBytes, err := os.ReadFile(config.MacaroonPath)
if err != nil {
return nil, err
}
mac := &macaroon.Macaroon{}
if err := mac.UnmarshalBinary(macBytes); err != nil {
return nil, err
}
macaroon, err := macaroons.NewMacaroonCredential(mac)
if err != nil {
return nil, err
}
// Create the DialOptions with the macaroon credentials.
opts := []grpc.DialOption{
grpc.WithTransportCredentials(creds),
grpc.WithPerRPCCredentials(macaroon),
grpc.WithDefaultCallOptions(maxMsgRecvSize),
}
// Dial the gRPC server.
conn, err := grpc.Dial(config.Host, opts...)
if err != nil {
return nil, err
}
return conn, nil
}

120
client.go
View file

@ -14,6 +14,7 @@ import (
"github.com/btcsuite/btcd/btcutil"
"github.com/lightninglabs/aperture/l402"
"github.com/lightninglabs/lndclient"
"github.com/lightninglabs/loop/assets"
"github.com/lightninglabs/loop/loopdb"
"github.com/lightninglabs/loop/swap"
"github.com/lightninglabs/loop/sweep"
@ -78,6 +79,13 @@ var (
// quote call as the miner fee if the fee estimation in lnd's wallet
// failed because of insufficient funds.
MinerFeeEstimationFailed btcutil.Amount = -1
// defaultRFQExpiry is the default expiry time for RFQs.
defaultRFQExpiry = 5 * time.Minute
// defaultRFQMaxLimitMultiplier is the default maximum fee multiplier for
// RFQs.
defaultRFQMaxLimitMultiplier = 1.2
)
// Client performs the client side part of swaps. This interface exists to be
@ -94,6 +102,7 @@ type Client struct {
lndServices *lndclient.LndServices
sweeper *sweep.Sweeper
executor *executor
assetClient *assets.TapdClient
resumeReady chan struct{}
wg sync.WaitGroup
@ -121,6 +130,9 @@ type ClientConfig struct {
// Lnd is an instance of the lnd proxy.
Lnd *lndclient.LndServices
// AssetClient is an instance of the assets client.
AssetClient *assets.TapdClient
// MaxL402Cost is the maximum price we are willing to pay to the server
// for the token.
MaxL402Cost btcutil.Amount
@ -273,6 +285,7 @@ func NewClient(dbDir string, loopDB loopdb.SwapStore,
errChan: make(chan error),
clientConfig: *config,
lndServices: cfg.Lnd,
assetClient: cfg.AssetClient,
sweeper: sweeper,
executor: executor,
resumeReady: make(chan struct{}),
@ -336,6 +349,10 @@ func (s *Client) FetchSwaps(ctx context.Context) ([]*SwapInfo, error) {
return nil, swap.ErrInvalidOutputType
}
if swp.Contract.AssetSwapInfo != nil {
swapInfo.AssetSwapInfo = swp.Contract.AssetSwapInfo
}
swaps = append(swaps, swapInfo)
}
@ -453,7 +470,7 @@ func (s *Client) Run(ctx context.Context, statusChan chan<- SwapInfo) error {
func (s *Client) resumeSwaps(ctx context.Context,
loopOutSwaps []*loopdb.LoopOut, loopInSwaps []*loopdb.LoopIn) {
swapCfg := newSwapConfig(s.lndServices, s.Store, s.Server)
swapCfg := newSwapConfig(s.lndServices, s.Store, s.Server, s.assetClient)
for _, pend := range loopOutSwaps {
if pend.State().State.Type() != loopdb.StateTypePending {
@ -500,9 +517,30 @@ func (s *Client) resumeSwaps(ctx context.Context,
func (s *Client) LoopOut(globalCtx context.Context,
request *OutRequest) (*LoopOutSwapInfo, error) {
log.Infof("LoopOut %v to %v (channels: %v)",
request.Amount, request.DestAddr, request.OutgoingChanSet,
)
if request.AssetId != nil {
if request.AssetPrepayRfqId == nil ||
request.AssetSwapRfqId == nil {
return nil, errors.New("asset prepay and swap rfq ids " +
"must be set when using an asset id")
}
// Verify that if we have an asset id set, we have a valid asset
// client to use.
if s.assetClient == nil {
return nil, errors.New("asset client must be set " +
"when using an asset id")
}
log.Infof("LoopOut %v sats to %v with asset %x",
request.Amount, request.DestAddr, request.AssetId,
)
} else {
log.Infof("LoopOut %v to %v (channels: %v)",
request.Amount, request.DestAddr,
request.OutgoingChanSet,
)
}
if err := s.waitForInitialized(globalCtx); err != nil {
return nil, err
@ -523,7 +561,10 @@ func (s *Client) LoopOut(globalCtx context.Context,
}
// Create a new swap object for this swap.
swapCfg := newSwapConfig(s.lndServices, s.Store, s.Server)
swapCfg := newSwapConfig(
s.lndServices, s.Store, s.Server, s.assetClient,
)
initResult, err := newLoopOutSwap(
globalCtx, swapCfg, initiationHeight, request,
)
@ -568,6 +609,14 @@ func (s *Client) getExpiry(height int32, terms *LoopOutTerms,
func (s *Client) LoopOutQuote(ctx context.Context,
request *LoopOutQuoteRequest) (*LoopOutQuote, error) {
if request.AssetRFQRequest != nil {
rfqReq := request.AssetRFQRequest
if rfqReq.AssetId == nil || rfqReq.AssetEdgeNode == nil {
return nil, errors.New("both asset edge node and " +
"asset id must be set")
}
}
terms, err := s.Server.GetLoopOutTerms(ctx, request.Initiator)
if err != nil {
return nil, err
@ -602,12 +651,67 @@ func (s *Client) LoopOutQuote(ctx context.Context,
return nil, err
}
return &LoopOutQuote{
loopOutQuote := &LoopOutQuote{
SwapFee: quote.SwapFee,
MinerFee: minerFee,
PrepayAmount: quote.PrepayAmount,
SwapPaymentDest: quote.SwapPaymentDest,
}, nil
}
// If we use an Asset we'll rfq to get the asset amounts to use for
// the swap.
if request.AssetRFQRequest != nil {
rfqReq := request.AssetRFQRequest
if rfqReq.Expiry == 0 {
rfqReq.Expiry = time.Now().Add(defaultRFQExpiry).Unix()
}
if rfqReq.MaxLimitMultiplier == 0 {
rfqReq.MaxLimitMultiplier = defaultRFQMaxLimitMultiplier
}
// First we'll get the prepay rfq.
prepayRfq, err := s.assetClient.GetRfqForAsset(
ctx, quote.PrepayAmount, rfqReq.AssetId,
rfqReq.AssetEdgeNode, rfqReq.Expiry,
rfqReq.MaxLimitMultiplier,
)
if err != nil {
return nil, err
}
// The actual invoice swap amount is the requested amount plus
// the swap fee minus the prepay amount.
invoiceAmt := request.Amount + quote.SwapFee -
quote.PrepayAmount
swapRfq, err := s.assetClient.GetRfqForAsset(
ctx, invoiceAmt, rfqReq.AssetId,
rfqReq.AssetEdgeNode, rfqReq.Expiry,
rfqReq.MaxLimitMultiplier,
)
if err != nil {
return nil, err
}
// We'll also want the asset name to verify for the client.
assetName, err := s.assetClient.GetAssetName(
ctx, rfqReq.AssetId,
)
if err != nil {
return nil, err
}
loopOutQuote.LoopOutRfq = &LoopOutRfq{
PrepayRfqId: prepayRfq.Id,
PrepayAssetAmt: prepayRfq.AssetAmount,
SwapRfqId: swapRfq.Id,
SwapAssetAmt: swapRfq.AssetAmount,
AssetName: assetName,
}
}
return loopOutQuote, nil
}
// getLoopOutSweepFee is a helper method to estimate the loop out htlc sweep
@ -682,7 +786,7 @@ func (s *Client) LoopIn(globalCtx context.Context,
// Create a new swap object for this swap.
initiationHeight := s.executor.height()
swapCfg := newSwapConfig(s.lndServices, s.Store, s.Server)
swapCfg := newSwapConfig(s.lndServices, s.Store, s.Server, s.assetClient)
initResult, err := newLoopInSwap(
globalCtx, swapCfg, initiationHeight, request,
)

View file

@ -2,6 +2,7 @@ package main
import (
"context"
"encoding/hex"
"fmt"
"math"
"strconv"
@ -101,6 +102,20 @@ var loopOutCommand = cli.Command{
"payment might be retried, the actual total " +
"time may be longer",
},
cli.StringFlag{
Name: "asset_id",
Usage: "the asset ID of the asset to loop out, " +
"if this is set, the loop daemon will " +
"require a connection to a taproot assets " +
"daemon",
},
cli.StringFlag{
Name: "asset_edge_node",
Usage: "the pubkey of the edge node of the asset to " +
"loop out, this is required if the taproot " +
"assets daemon has multiple channels of the " +
"given asset id with different edge nodes",
},
forceFlag,
labelFlag,
verboseFlag,
@ -134,6 +149,10 @@ func loopOut(ctx *cli.Context) error {
// element.
var outgoingChanSet []uint64
if ctx.IsSet("channel") {
if ctx.IsSet("asset_id") {
return fmt.Errorf("channel flag is not supported when " +
"looping out assets")
}
chanStrings := strings.Split(ctx.String("channel"), ",")
for _, chanString := range chanStrings {
chanID, err := strconv.ParseUint(chanString, 10, 64)
@ -186,6 +205,33 @@ func loopOut(ctx *cli.Context) error {
}
}
var assetLoopOutInfo *looprpc.AssetLoopOutRequest
var assetId []byte
if ctx.IsSet("asset_id") {
if !ctx.IsSet("asset_edge_node") {
return fmt.Errorf("asset edge node is required when " +
"assetid is set")
}
assetId, err = hex.DecodeString(ctx.String("asset_id"))
if err != nil {
return err
}
assetEdgeNode, err := hex.DecodeString(
ctx.String("asset_edge_node"),
)
if err != nil {
return err
}
assetLoopOutInfo = &looprpc.AssetLoopOutRequest{
AssetId: assetId,
AssetEdgeNode: assetEdgeNode,
}
}
client, cleanup, err := getClient(ctx)
if err != nil {
return err
@ -210,6 +256,7 @@ func loopOut(ctx *cli.Context) error {
Amt: int64(amt),
ConfTarget: sweepConfTarget,
SwapPublicationDeadline: uint64(swapDeadline.Unix()),
AssetInfo: assetLoopOutInfo,
}
quote, err := client.LoopOutQuote(context.Background(), quoteReq)
if err != nil {
@ -281,6 +328,8 @@ func loopOut(ctx *cli.Context) error {
Label: label,
Initiator: defaultInitiator,
PaymentTimeout: uint32(paymentTimeout),
AssetInfo: assetLoopOutInfo,
AssetRfqInfo: quote.AssetRfqInfo,
})
if err != nil {
return err

View file

@ -96,6 +96,13 @@ const (
// Estimated on-chain fee: 7262 sat
satAmtFmt = "%-36s %12d sat\n"
// assetAmtFormat formats a value into a one line string, intended to
// prettify the terminal output. For Instance,
// fmt.Printf(f, "Amount:", amt, "USD")
// prints out as,
// Amount: 50 USD
assetAmtFmt = "%-36s %12d %s\n"
// blkFmt formats the number of blocks into a one line string, intended
// to prettify the terminal output. For Instance,
// fmt.Printf(f, "Conf target", target)

View file

@ -267,7 +267,14 @@ func printQuoteOutResp(req *looprpc.QuoteRequest,
totalFee := resp.HtlcSweepFeeSat + resp.SwapFeeSat
fmt.Printf(satAmtFmt, "Send off-chain:", req.Amt)
if resp.AssetRfqInfo != nil {
fmt.Printf(assetAmtFmt, "Send off-chain:",
resp.AssetRfqInfo.SwapAssetAmt,
resp.AssetRfqInfo.AssetName)
} else {
fmt.Printf(satAmtFmt, "Send off-chain:", req.Amt)
}
fmt.Printf(satAmtFmt, "Receive on-chain:", req.Amt-totalFee)
if !verbose {
@ -280,7 +287,14 @@ func printQuoteOutResp(req *looprpc.QuoteRequest,
fmt.Printf(satAmtFmt, "Loop service fee:", resp.SwapFeeSat)
fmt.Printf(satAmtFmt, "Estimated total fee:", totalFee)
fmt.Println()
fmt.Printf(satAmtFmt, "No show penalty (prepay):", resp.PrepayAmtSat)
if resp.AssetRfqInfo != nil {
fmt.Printf(assetAmtFmt, "No show penalty (prepay):",
resp.AssetRfqInfo.PrepayAssetAmt,
resp.AssetRfqInfo.AssetName)
} else {
fmt.Printf(satAmtFmt, "No show penalty (prepay):",
resp.PrepayAmtSat)
}
fmt.Printf(blkFmt, "Conf target:", resp.ConfTarget)
fmt.Printf(blkFmt, "CLTV expiry delta:", resp.CltvDelta)
fmt.Printf("%-38s %s\n",

93
go.mod
View file

@ -6,28 +6,30 @@ require (
github.com/btcsuite/btcd/btcutil v1.1.5
github.com/btcsuite/btcd/btcutil/psbt v1.1.8
github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f
github.com/btcsuite/btcwallet v0.16.10-0.20240809133323-7d3434c65ae2
github.com/btcsuite/btcwallet/wtxmgr v1.5.3
github.com/btcsuite/btclog v0.0.0-20241003133417-09c4e92e319c
github.com/btcsuite/btcwallet v0.16.10-0.20240912233857-ffb143c77cc5
github.com/btcsuite/btcwallet/wtxmgr v1.5.4
github.com/coreos/bbolt v1.3.3
github.com/davecgh/go-spew v1.1.1
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0
github.com/fortytw2/leaktest v1.3.0
github.com/golang-migrate/migrate/v4 v4.17.0
github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0
github.com/jackc/pgconn v1.14.3
github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438
github.com/jessevdk/go-flags v1.4.0
github.com/lib/pq v1.10.9
github.com/lightninglabs/aperture v0.3.2-beta
github.com/lightninglabs/lndclient v0.18.4-0
github.com/lightninglabs/aperture v0.3.4-beta
github.com/lightninglabs/lndclient v0.18.4-9
github.com/lightninglabs/loop/looprpc v1.0.0
github.com/lightninglabs/loop/swapserverrpc v1.0.11
github.com/lightningnetwork/lnd v0.18.3-beta.rc3.0.20241011124628-ca3bde901eb8
github.com/lightninglabs/taproot-assets v0.5.0
github.com/lightningnetwork/lnd v0.18.4-beta
github.com/lightningnetwork/lnd/cert v1.2.2
github.com/lightningnetwork/lnd/clock v1.1.1
github.com/lightningnetwork/lnd/queue v1.1.1
github.com/lightningnetwork/lnd/ticker v1.1.1
github.com/lightningnetwork/lnd/tlv v1.2.6
github.com/lightningnetwork/lnd/tor v1.1.2
github.com/ory/dockertest/v3 v3.10.0
github.com/stretchr/testify v1.9.0
@ -37,11 +39,11 @@ require (
google.golang.org/protobuf v1.34.2
gopkg.in/macaroon-bakery.v2 v2.1.0
gopkg.in/macaroon.v2 v2.1.0
modernc.org/sqlite v1.29.10
modernc.org/sqlite v1.30.0
)
require (
dario.cat/mergo v1.0.0 // indirect
dario.cat/mergo v1.0.1 // indirect
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/NebulousLabs/fastrand v0.0.0-20181203155948-6fb6489aac4e // indirect
@ -51,33 +53,34 @@ require (
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect
github.com/aead/siphash v1.0.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/btcsuite/btcwallet/wallet/txauthor v1.3.4 // indirect
github.com/btcsuite/btcwallet/wallet/txrules v1.2.1 // indirect
github.com/btcsuite/btcwallet/wallet/txsizes v1.2.4 // indirect
github.com/btcsuite/btcwallet/walletdb v1.4.2 // indirect
github.com/btcsuite/btcwallet/wallet/txauthor v1.3.5 // indirect
github.com/btcsuite/btcwallet/wallet/txrules v1.2.2 // indirect
github.com/btcsuite/btcwallet/wallet/txsizes v1.2.5 // indirect
github.com/btcsuite/btcwallet/walletdb v1.4.4 // indirect
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd // indirect
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 // indirect
github.com/btcsuite/winsvc v1.0.0 // indirect
github.com/cenkalti/backoff/v4 v4.1.3 // indirect
github.com/caddyserver/certmagic v0.17.2 // indirect
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/containerd/continuity v0.3.0 // indirect
github.com/coreos/go-semver v0.3.0 // indirect
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f // indirect
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf // indirect
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
github.com/decred/dcrd/crypto/blake256 v1.0.1 // indirect
github.com/decred/dcrd/lru v1.1.2 // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/docker/cli v27.1.1+incompatible // indirect
github.com/docker/docker v27.1.1+incompatible // indirect
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fergusstrange/embedded-postgres v1.25.0 // indirect
github.com/go-errors/errors v1.0.1 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-viper/mapstructure/v2 v2.0.0 // indirect
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v4 v4.5.1 // indirect
github.com/golang/protobuf v1.5.4 // indirect
@ -86,7 +89,9 @@ require (
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.0-rc.0 // indirect
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.0-rc.3 // indirect
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
@ -108,19 +113,22 @@ require (
github.com/json-iterator/go v1.1.12 // indirect
github.com/juju/loggo v0.0.0-20210728185423-eebad3a902c4 // indirect
github.com/kkdai/bstream v1.0.0 // indirect
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
github.com/libdns/libdns v0.2.1 // indirect
github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf // indirect
github.com/lightninglabs/lightning-node-connect/hashmailrpc v1.0.2 // indirect
github.com/lightninglabs/neutrino v0.16.1-0.20240425105051-602843d34ffd // indirect
github.com/lightninglabs/neutrino/cache v1.1.2 // indirect
github.com/lightningnetwork/lightning-onion v1.2.1-0.20240712235311-98bd56499dfb // indirect
github.com/lightningnetwork/lnd/fn v1.2.1 // indirect
github.com/lightningnetwork/lnd/fn v1.2.3 // indirect
github.com/lightningnetwork/lnd/healthcheck v1.2.5 // indirect
github.com/lightningnetwork/lnd/kvdb v1.4.10 // indirect
github.com/lightningnetwork/lnd/sqldb v1.0.4 // indirect
github.com/lightningnetwork/lnd/tlv v1.2.6 // indirect
github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
github.com/miekg/dns v1.1.43 // indirect
github.com/mholt/acmez v1.0.4 // indirect
github.com/miekg/dns v1.1.50 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/term v0.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
@ -131,10 +139,10 @@ require (
github.com/opencontainers/runc v1.1.14 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.11.1 // indirect
github.com/prometheus/client_golang v1.14.0 // indirect
github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.30.0 // indirect
github.com/prometheus/procfs v0.7.3 // indirect
github.com/prometheus/common v0.37.0 // indirect
github.com/prometheus/procfs v0.8.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rogpeppe/fastuuid v1.2.0 // indirect
github.com/russross/blackfriday/v2 v2.0.1 // indirect
@ -152,27 +160,25 @@ require (
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect
gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect
go.etcd.io/bbolt v1.3.7 // indirect
go.etcd.io/etcd/api/v3 v3.5.7 // indirect
go.etcd.io/etcd/client/pkg/v3 v3.5.7 // indirect
go.etcd.io/etcd/client/v2 v2.305.7 // indirect
go.etcd.io/etcd/client/v3 v3.5.7 // indirect
go.etcd.io/etcd/pkg/v3 v3.5.7 // indirect
go.etcd.io/etcd/raft/v3 v3.5.7 // indirect
go.etcd.io/etcd/server/v3 v3.5.7 // indirect
go.etcd.io/bbolt v1.3.11 // indirect
go.etcd.io/etcd/api/v3 v3.5.12 // indirect
go.etcd.io/etcd/client/pkg/v3 v3.5.12 // indirect
go.etcd.io/etcd/client/v2 v2.305.12 // indirect
go.etcd.io/etcd/client/v3 v3.5.12 // indirect
go.etcd.io/etcd/pkg/v3 v3.5.12 // indirect
go.etcd.io/etcd/raft/v3 v3.5.12 // indirect
go.etcd.io/etcd/server/v3 v3.5.12 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect
go.opentelemetry.io/otel v1.28.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.3.0 // indirect
go.opentelemetry.io/otel/metric v1.28.0 // indirect
go.opentelemetry.io/otel v1.32.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.20.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.20.0 // indirect
go.opentelemetry.io/otel/metric v1.32.0 // indirect
go.opentelemetry.io/otel/sdk v1.21.0 // indirect
go.opentelemetry.io/otel/trace v1.28.0 // indirect
go.opentelemetry.io/proto/otlp v0.19.0 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.opentelemetry.io/otel/trace v1.32.0 // indirect
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.17.0 // indirect
go.uber.org/zap v1.23.0 // indirect
golang.org/x/crypto v0.31.0 // indirect
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 // indirect
golang.org/x/mod v0.17.0 // indirect
@ -190,11 +196,12 @@ require (
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect
modernc.org/libc v1.49.3 // indirect
modernc.org/libc v1.50.9 // indirect
modernc.org/mathutil v1.6.0 // indirect
modernc.org/memory v1.8.0 // indirect
modernc.org/strutil v1.2.0 // indirect
modernc.org/token v1.1.0 // indirect
pgregory.net/rapid v1.1.0 // indirect
sigs.k8s.io/yaml v1.2.0 // indirect
)

232
go.sum
View file

@ -596,8 +596,8 @@ cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoIS
cloud.google.com/go/workflows v1.8.0/go.mod h1:ysGhmEajwZxGn1OhGOGKsTXc5PyxOc0vfKf5Af+to4M=
cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA=
cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcPALq2CxzdePw=
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8=
git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc=
@ -639,6 +639,8 @@ github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kd
github.com/apache/arrow/go/v10 v10.0.1/go.mod h1:YvhnlEePVnBS4+0z3fhPfUy7W1Ikj0Ih0vcRo/gZ1M0=
github.com/apache/arrow/go/v11 v11.0.0/go.mod h1:Eg5OsL5H+e299f7u5ssuXsuHQVEGC4xei5aX110hRiI=
github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
@ -664,21 +666,22 @@ github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtyd
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 h1:59Kx4K6lzOW5w6nFlA0v5+lk/6sjybR934QNHSJZPTQ=
github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo=
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
github.com/btcsuite/btclog v0.0.0-20241003133417-09c4e92e319c h1:4HxD1lBUGUddhzgaNgrCPsFWd7cGYNpeFUgd9ZIgyM0=
github.com/btcsuite/btclog v0.0.0-20241003133417-09c4e92e319c/go.mod h1:w7xnGOhwT3lmrS4H3b/D1XAXxvh+tbhUm8xeHN2y3TQ=
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
github.com/btcsuite/btcwallet v0.16.10-0.20240809133323-7d3434c65ae2 h1:qa4Avm7p97JroZZyMJADbEb9u853pjleJYSeitENvLc=
github.com/btcsuite/btcwallet v0.16.10-0.20240809133323-7d3434c65ae2/go.mod h1:X2xDre+j1QphTRo54y2TikUzeSvreL1t1aMXrD8Kc5A=
github.com/btcsuite/btcwallet/wallet/txauthor v1.3.4 h1:poyHFf7+5+RdxNp5r2T6IBRD7RyraUsYARYbp/7t4D8=
github.com/btcsuite/btcwallet/wallet/txauthor v1.3.4/go.mod h1:GETGDQuyq+VFfH1S/+/7slLM/9aNa4l7P4ejX6dJfb0=
github.com/btcsuite/btcwallet/wallet/txrules v1.2.1 h1:UZo7YRzdHbwhK7Rhv3PO9bXgTxiOH45edK5qdsdiatk=
github.com/btcsuite/btcwallet/wallet/txrules v1.2.1/go.mod h1:MVSqRkju/IGxImXYPfBkG65FgEZYA4fXchheILMVl8g=
github.com/btcsuite/btcwallet/wallet/txsizes v1.2.4 h1:nmcKAVTv/cmYrs0A4hbiC6Qw+WTLYy/14SmTt3mLnCo=
github.com/btcsuite/btcwallet/wallet/txsizes v1.2.4/go.mod h1:YqJR8WAAHiKIPesZTr9Cx9Az4fRhRLcJ6GcxzRUZCAc=
github.com/btcsuite/btcwallet/walletdb v1.4.2 h1:zwZZ+zaHo4mK+FAN6KeK85S3oOm+92x2avsHvFAhVBE=
github.com/btcsuite/btcwallet/walletdb v1.4.2/go.mod h1:7ZQ+BvOEre90YT7eSq8bLoxTsgXidUzA/mqbRS114CQ=
github.com/btcsuite/btcwallet/wtxmgr v1.5.3 h1:QrWCio9Leh3DwkWfp+A1SURj8pYn3JuTLv3waP5uEro=
github.com/btcsuite/btcwallet/wtxmgr v1.5.3/go.mod h1:M4nQpxGTXiDlSOODKXboXX7NFthmiBNjzAKKNS7Fhjg=
github.com/btcsuite/btcwallet v0.16.10-0.20240912233857-ffb143c77cc5 h1:zYy233eUBvkF3lq2MUkybEhxhDsrRDSgiToIKN57mtk=
github.com/btcsuite/btcwallet v0.16.10-0.20240912233857-ffb143c77cc5/go.mod h1:1HJXYbjJzgumlnxOC2+ViR1U+gnHWoOn7WeK5OfY1eU=
github.com/btcsuite/btcwallet/wallet/txauthor v1.3.5 h1:Rr0njWI3r341nhSPesKQ2JF+ugDSzdPoeckS75SeDZk=
github.com/btcsuite/btcwallet/wallet/txauthor v1.3.5/go.mod h1:+tXJ3Ym0nlQc/iHSwW1qzjmPs3ev+UVWMbGgfV1OZqU=
github.com/btcsuite/btcwallet/wallet/txrules v1.2.2 h1:YEO+Lx1ZJJAtdRrjuhXjWrYsmAk26wLTlNzxt2q0lhk=
github.com/btcsuite/btcwallet/wallet/txrules v1.2.2/go.mod h1:4v+grppsDpVn91SJv+mZT7B8hEV4nSmpREM4I8Uohws=
github.com/btcsuite/btcwallet/wallet/txsizes v1.2.5 h1:93o5Xz9dYepBP4RMFUc9RGIFXwqP2volSWRkYJFrNtI=
github.com/btcsuite/btcwallet/wallet/txsizes v1.2.5/go.mod h1:lQ+e9HxZ85QP7r3kdxItkiMSloSLg1PEGis5o5CXUQw=
github.com/btcsuite/btcwallet/walletdb v1.4.4 h1:BDel6iT/ltYSIYKs0YbjwnEDi7xR3yzABIsQxN2F1L8=
github.com/btcsuite/btcwallet/walletdb v1.4.4/go.mod h1:jk/hvpLFINF0C1kfTn0bfx2GbnFT+Nvnj6eblZALfjs=
github.com/btcsuite/btcwallet/wtxmgr v1.5.4 h1:hJjHy1h/dJwSfD9uDsCwcH21D1iOrus6OrI5gR9E/O0=
github.com/btcsuite/btcwallet/wtxmgr v1.5.4/go.mod h1:lAv0b1Vj9Ig5U8QFm0yiJ9WqPl8yGO/6l7JxdHY1PKE=
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd h1:R/opQEbFEy9JGkIguV40SvRY1uliPX8ifOvi6ICsFCw=
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
github.com/btcsuite/golangcrypto v0.0.0-20150304025918-53f62d9b43e8/go.mod h1:tYvUd8KLhm/oXvUeSEs2VlLghFjQt9+ZaF9ghH0JNjc=
@ -690,16 +693,16 @@ github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 h1:R8vQdOQdZ9Y3
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
github.com/btcsuite/winsvc v1.0.0 h1:J9B4L7e3oqhXOcm+2IuNApwzQec85lE+QaikUcCs+dk=
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4=
github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
github.com/caddyserver/certmagic v0.17.2 h1:o30seC1T/dBqBCNNGNHWwj2i5/I/FMjBbTAhjADP3nE=
github.com/caddyserver/certmagic v0.17.2/go.mod h1:ouWUuC490GOLJzkyN35eXfV8bSbwMwSf4bdhkIxtdQE=
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw=
github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054 h1:uH66TXeswKn5PW5zdZ39xEwfS9an067BirqA+P4QaLI=
github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
@ -723,12 +726,8 @@ github.com/cncf/xds/go v0.0.0-20240318125728-8a4994d93e50 h1:DBmgJDC9dTfkVyGgipa
github.com/cncf/xds/go v0.0.0-20240318125728-8a4994d93e50/go.mod h1:5e1+Vvlzido69INQaVO6d87Qn543Xr6nooe9Kz7oBFM=
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5 h1:xD/lrqdvwsc+O2bjSSi3YqY73Ke3LAiSCx49aCesA0E=
github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo=
github.com/cockroachdb/errors v1.2.4 h1:Lap807SXTH5tri2TivECb/4abUkMZC9zRoLarvcKDqs=
github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA=
github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f h1:o/kfcElHqOiXqcou5a3rIlMc7oJbMQkeLk0VQJ7zgqY=
github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI=
github.com/cockroachdb/datadriven v1.0.2 h1:H9MtNqVoVhvd9nCBwOyDjUEdZCREqbIdCJD93PBm/jA=
github.com/cockroachdb/datadriven v1.0.2/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU=
github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg=
github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM=
github.com/coreos/bbolt v1.3.3 h1:n6AiVyVRKQFNb6mJlwESEvvLoDyiTzXX7ORAUlkeBdY=
@ -736,8 +735,9 @@ github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkE
github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f h1:JOrtw2xFKzlg+cbHpyrpLDmnN1HqhBfnX7WDiW7eG2c=
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pqaw19uhpSCzhj44qo35pNgKFGqzDKkU=
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
@ -809,8 +809,6 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
github.com/getsentry/raven-go v0.2.0 h1:no+xWJRb5ZI7eE8TWgIq1jLulQiIoLG0IfYxv5JYMGs=
github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
@ -825,17 +823,16 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U=
github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81/go.mod h1:SX0U8uGpxhq9o2S/CELCSUxEWWAuoCUcVCQWv7G2OCk=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M=
@ -843,8 +840,8 @@ github.com/go-pdf/fpdf v0.6.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhO
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-viper/mapstructure/v2 v2.0.0 h1:dhn8MZ1gZ0mzeodTG3jt5Vj/o87xZKuNAprG2mQfMfc=
github.com/go-viper/mapstructure/v2 v2.0.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
@ -859,6 +856,7 @@ github.com/golang-migrate/migrate/v4 v4.17.0 h1:rd40H3QXU0AA4IoLllFcEAEo9dYKRHYN
github.com/golang-migrate/migrate/v4 v4.17.0/go.mod h1:+Cp2mtLP4/aXDTKb9wmXYitdrNx2HGs45rbWAo6OsKM=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/glog v0.0.0-20210429001901-424d2337a529/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ=
github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68=
@ -967,15 +965,21 @@ github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw=
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI=
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8=
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.0-rc.0 h1:mdLirNAJBxnGgyB6pjZLcs6ue/6eZGBui6gXspfq4ks=
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.0-rc.0/go.mod h1:kdXbOySqcQeTxiqglW7aahTmWZy3Pgi6SYL36yvKeyA=
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.0-rc.3 h1:o95KDiV/b1xdkumY5YbLR0/n2+wBxUpgf3HgfKgTyLI=
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.0-rc.3/go.mod h1:hTxjzRcX49ogbTGVJ1sM5mz5s+SSgiGIyL3jjPxl32E=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.5.0/go.mod h1:r1hZAcvfFXuYmcKyCJI9wlyOPIZUJl6FCB8Cpca/NLE=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3 h1:lLT7ZLSzGLI08vc9cpd+tYmNWjdKDqyr/2L+f6U12Fk=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@ -1094,6 +1098,8 @@ github.com/kkdai/bstream v1.0.0/go.mod h1:FDnDOHt5Yx4p3FaHcioFT0QjDOtgUpvjeZqAs+
github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE=
github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@ -1115,28 +1121,34 @@ github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lightninglabs/aperture v0.3.2-beta h1:J2GQwBmSHxpr5VOatXbgrTogF/qN2l6UWLPHfIowq10=
github.com/lightninglabs/aperture v0.3.2-beta/go.mod h1:M/5dPzHjHvuYXQuxzicqaGiCclHUvKW6N0ay1t/HGiM=
github.com/libdns/libdns v0.2.1 h1:Wu59T7wSHRgtA0cfxC+n1c/e+O3upJGWytknkmFEDis=
github.com/libdns/libdns v0.2.1/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
github.com/lightninglabs/aperture v0.3.4-beta h1:TiQHw1+2CdW785U88uH0BmwUmv0R7nyfvmF9neQd56o=
github.com/lightninglabs/aperture v0.3.4-beta/go.mod h1:xuusZUPdKzQN8wKT5yL2eML8To9nz+AXoRoQa/njd4Q=
github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf h1:HZKvJUHlcXI/f/O0Avg7t8sqkPo78HFzjmeYFl6DPnc=
github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf/go.mod h1:vxmQPeIQxPf6Jf9rM8R+B4rKBqLA2AjttNxkFBL2Plk=
github.com/lightninglabs/lndclient v0.18.4-0 h1:TdorvV9UIw3fjZrNpVKn3fpsOdw2KWF2Eqdx7+++lcY=
github.com/lightninglabs/lndclient v0.18.4-0/go.mod h1:LbINSPfKEdZuTGqqJ+ZmUxXWNvUCaDqrZeJ7/Al0Z3Y=
github.com/lightninglabs/lightning-node-connect/hashmailrpc v1.0.2 h1:Er1miPZD2XZwcfE4xoS5AILqP1mj7kqnhbBSxW9BDxY=
github.com/lightninglabs/lightning-node-connect/hashmailrpc v1.0.2/go.mod h1:antQGRDRJiuyQF6l+k6NECCSImgCpwaZapATth2Chv4=
github.com/lightninglabs/lndclient v0.18.4-9 h1:8PRBmJLyegs1zbqBvpN/0d8qGLiNst3MEtTdO4Wt+SA=
github.com/lightninglabs/lndclient v0.18.4-9/go.mod h1:11hKoRxXk+1IoIndEIvbmo18dwAJnTqr/lylRpYDVSU=
github.com/lightninglabs/neutrino v0.16.1-0.20240425105051-602843d34ffd h1:D8aRocHpoCv43hL8egXEMYyPmyOiefFHZ66338KQB2s=
github.com/lightninglabs/neutrino v0.16.1-0.20240425105051-602843d34ffd/go.mod h1:x3OmY2wsA18+Kc3TSV2QpSUewOCiscw2mKpXgZv2kZk=
github.com/lightninglabs/neutrino/cache v1.1.2 h1:C9DY/DAPaPxbFC+xNNEI/z1SJY9GS3shmlu5hIQ798g=
github.com/lightninglabs/neutrino/cache v1.1.2/go.mod h1:XJNcgdOw1LQnanGjw8Vj44CvguYA25IMKjWFZczwZuo=
github.com/lightninglabs/protobuf-go-hex-display v1.33.0-hex-display h1:Y2WiPkBS/00EiEg0qp0FhehxnQfk3vv8U6Xt3nN+rTY=
github.com/lightninglabs/protobuf-go-hex-display v1.33.0-hex-display/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
github.com/lightninglabs/taproot-assets v0.5.0 h1:aXX08DsV9ObUCm5jAbUsd0B1llcAqQHVAfrxtRu+q7Q=
github.com/lightninglabs/taproot-assets v0.5.0/go.mod h1:7XEbJ8DZ79hkYUNuZceuynT6vGqDNdAn7l20knK9DfI=
github.com/lightningnetwork/lightning-onion v1.2.1-0.20240712235311-98bd56499dfb h1:yfM05S8DXKhuCBp5qSMZdtSwvJ+GFzl94KbXMNB1JDY=
github.com/lightningnetwork/lightning-onion v1.2.1-0.20240712235311-98bd56499dfb/go.mod h1:c0kvRShutpj3l6B9WtTsNTBUtjSmjZXbJd9ZBRQOSKI=
github.com/lightningnetwork/lnd v0.18.3-beta.rc3.0.20241011124628-ca3bde901eb8 h1:+z0s8M0QItH51qMPgFGlRvi6uBltbURQj6u1srTyRb4=
github.com/lightningnetwork/lnd v0.18.3-beta.rc3.0.20241011124628-ca3bde901eb8/go.mod h1:gzVQkOCZxTLzlUPqnI6t68FVGLbiO6Jj+TcLb4b78n0=
github.com/lightningnetwork/lnd v0.18.4-beta h1:4pGmIjIMisrs4TMDYp4fk8NeI1YFpcuqwaSiFwLcd1g=
github.com/lightningnetwork/lnd v0.18.4-beta/go.mod h1:nPRQzLla5uHPQFyyZn8r9Vgddkd23PBUDa9rggEPOfY=
github.com/lightningnetwork/lnd/cert v1.2.2 h1:71YK6hogeJtxSxw2teq3eGeuy4rHGKcFf0d0Uy4qBjI=
github.com/lightningnetwork/lnd/cert v1.2.2/go.mod h1:jQmFn/Ez4zhDgq2hnYSw8r35bqGVxViXhX6Cd7HXM6U=
github.com/lightningnetwork/lnd/clock v1.1.1 h1:OfR3/zcJd2RhH0RU+zX/77c0ZiOnIMsDIBjgjWdZgA0=
github.com/lightningnetwork/lnd/clock v1.1.1/go.mod h1:mGnAhPyjYZQJmebS7aevElXKTFDuO+uNFFfMXK1W8xQ=
github.com/lightningnetwork/lnd/fn v1.2.1 h1:pPsVGrwi9QBwdLJzaEGK33wmiVKOxs/zc8H7+MamFf0=
github.com/lightningnetwork/lnd/fn v1.2.1/go.mod h1:SyFohpVrARPKH3XVAJZlXdVe+IwMYc4OMAvrDY32kw0=
github.com/lightningnetwork/lnd/fn v1.2.3 h1:Q1OrgNSgQynVheBNa16CsKVov1JI5N2AR6G07x9Mles=
github.com/lightningnetwork/lnd/fn v1.2.3/go.mod h1:SyFohpVrARPKH3XVAJZlXdVe+IwMYc4OMAvrDY32kw0=
github.com/lightningnetwork/lnd/healthcheck v1.2.5 h1:aTJy5xeBpcWgRtW/PGBDe+LMQEmNm/HQewlQx2jt7OA=
github.com/lightningnetwork/lnd/healthcheck v1.2.5/go.mod h1:G7Tst2tVvWo7cx6mSBEToQC5L1XOGxzZTPB29g9Rv2I=
github.com/lightningnetwork/lnd/kvdb v1.4.10 h1:vK89IVv1oVH9ubQWU+EmoCQFeVRaC8kfmOrqHbY5zoY=
@ -1172,8 +1184,10 @@ github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI=
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg=
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
github.com/mholt/acmez v1.0.4 h1:N3cE4Pek+dSolbsofIkAYz6H1d3pE+2G0os7QHslf80=
github.com/mholt/acmez v1.0.4/go.mod h1:qFGLZ4u+ehWINeJZjzPlsnjJBCPAADWTcIqE/7DAYQY=
github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY=
github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE=
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
@ -1234,8 +1248,9 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
github.com/prometheus/client_golang v1.11.1 h1:+4eQaD7vAZ6DsfsxB15hbE0odUjGI5ARs9yskGu1v4s=
github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw=
github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
@ -1245,14 +1260,16 @@ github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3d
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
github.com/prometheus/common v0.30.0 h1:JEkYlQnpzrzQFxi6gnukFPdQ+ac82oRhzMcIduJu/Ug=
github.com/prometheus/common v0.30.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE=
github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU=
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo=
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
@ -1342,22 +1359,22 @@ github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaD
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec h1:FpfFs4EhNehiVfzQttTuxanPIT43FtkkCFypIod8LHo=
gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec/go.mod h1:BZ1RAoRPbCxum9Grlv5aeksu2H8BiKehBYooU2LFiOQ=
go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ=
go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
go.etcd.io/etcd/api/v3 v3.5.7 h1:sbcmosSVesNrWOJ58ZQFitHMdncusIifYcrBfwrlJSY=
go.etcd.io/etcd/api/v3 v3.5.7/go.mod h1:9qew1gCdDDLu+VwmeG+iFpL+QlpHTo7iubavdVDgCAA=
go.etcd.io/etcd/client/pkg/v3 v3.5.7 h1:y3kf5Gbp4e4q7egZdn5T7W9TSHUvkClN6u+Rq9mEOmg=
go.etcd.io/etcd/client/pkg/v3 v3.5.7/go.mod h1:o0Abi1MK86iad3YrWhgUsbGx1pmTS+hrORWc2CamuhY=
go.etcd.io/etcd/client/v2 v2.305.7 h1:AELPkjNR3/igjbO7CjyF1fPuVPjrblliiKj+Y6xSGOU=
go.etcd.io/etcd/client/v2 v2.305.7/go.mod h1:GQGT5Z3TBuAQGvgPfhR7VPySu/SudxmEkRq9BgzFU6s=
go.etcd.io/etcd/client/v3 v3.5.7 h1:u/OhpiuCgYY8awOHlhIhmGIGpxfBU/GZBUP3m/3/Iz4=
go.etcd.io/etcd/client/v3 v3.5.7/go.mod h1:sOWmj9DZUMyAngS7QQwCyAXXAL6WhgTOPLNS/NabQgw=
go.etcd.io/etcd/pkg/v3 v3.5.7 h1:obOzeVwerFwZ9trMWapU/VjDcYUJb5OfgC1zqEGWO/0=
go.etcd.io/etcd/pkg/v3 v3.5.7/go.mod h1:kcOfWt3Ov9zgYdOiJ/o1Y9zFfLhQjylTgL4Lru8opRo=
go.etcd.io/etcd/raft/v3 v3.5.7 h1:aN79qxLmV3SvIq84aNTliYGmjwsW6NqJSnqmI1HLJKc=
go.etcd.io/etcd/raft/v3 v3.5.7/go.mod h1:TflkAb/8Uy6JFBxcRaH2Fr6Slm9mCPVdI2efzxY96yU=
go.etcd.io/etcd/server/v3 v3.5.7 h1:BTBD8IJUV7YFgsczZMHhMTS67XuA4KpRquL0MFOJGRk=
go.etcd.io/etcd/server/v3 v3.5.7/go.mod h1:gxBgT84issUVBRpZ3XkW1T55NjOb4vZZRI4wVvNhf4A=
go.etcd.io/bbolt v1.3.11 h1:yGEzV1wPz2yVCLsD8ZAiGHhHVlczyC9d1rP43/VCRJ0=
go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I=
go.etcd.io/etcd/api/v3 v3.5.12 h1:W4sw5ZoU2Juc9gBWuLk5U6fHfNVyY1WC5g9uiXZio/c=
go.etcd.io/etcd/api/v3 v3.5.12/go.mod h1:Ot+o0SWSyT6uHhA56al1oCED0JImsRiU9Dc26+C2a+4=
go.etcd.io/etcd/client/pkg/v3 v3.5.12 h1:EYDL6pWwyOsylrQyLp2w+HkQ46ATiOvoEdMarindU2A=
go.etcd.io/etcd/client/pkg/v3 v3.5.12/go.mod h1:seTzl2d9APP8R5Y2hFL3NVlD6qC/dOT+3kvrqPyTas4=
go.etcd.io/etcd/client/v2 v2.305.12 h1:0m4ovXYo1CHaA/Mp3X/Fak5sRNIWf01wk/X1/G3sGKI=
go.etcd.io/etcd/client/v2 v2.305.12/go.mod h1:aQ/yhsxMu+Oht1FOupSr60oBvcS9cKXHrzBpDsPTf9E=
go.etcd.io/etcd/client/v3 v3.5.12 h1:v5lCPXn1pf1Uu3M4laUE2hp/geOTc5uPcYYsNe1lDxg=
go.etcd.io/etcd/client/v3 v3.5.12/go.mod h1:tSbBCakoWmmddL+BKVAJHa9km+O/E+bumDe9mSbPiqw=
go.etcd.io/etcd/pkg/v3 v3.5.12 h1:OK2fZKI5hX/+BTK76gXSTyZMrbnARyX9S643GenNGb8=
go.etcd.io/etcd/pkg/v3 v3.5.12/go.mod h1:UVwg/QIMoJncyeb/YxvJBJCE/NEwtHWashqc8A1nj/M=
go.etcd.io/etcd/raft/v3 v3.5.12 h1:7r22RufdDsq2z3STjoR7Msz6fYH8tmbkdheGfwJNRmU=
go.etcd.io/etcd/raft/v3 v3.5.12/go.mod h1:ERQuZVe79PI6vcC3DlKBukDCLja/L7YMu29B74Iwj4U=
go.etcd.io/etcd/server/v3 v3.5.12 h1:EtMjsbfyfkwZuA2JlKOiBfuGkFCekv5H178qjXypbG8=
go.etcd.io/etcd/server/v3 v3.5.12/go.mod h1:axB0oCjMy+cemo5290/CutIjoxlfA6KVYKD1w0uue10=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
@ -1368,38 +1385,36 @@ go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 h1:SpGay3w+nEwMpfVnbqOLH5gY52/foP8RE8UzTZ1pdSE=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1/go.mod h1:4UoMYEZOC0yN/sPGH76KPkkU7zgiEWYWL9vwmbnTJPE=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg=
go.opentelemetry.io/otel v1.3.0/go.mod h1:PWIKzi6JCp7sM0k9yZ43VX+T345uNbAkDKwHVjb2PTs=
go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo=
go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4=
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0 h1:R/OBkMoGgfy2fLhs2QhkCI1w4HLEQX92GCcJB6SSdNk=
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0/go.mod h1:VpP4/RMn8bv8gNo9uK7/IMY4mtWLELsS+JIP0inH0h4=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0 h1:giGm8w67Ja7amYNfYMdme7xSp2pIxThWopw8+QP51Yk=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0/go.mod h1:hO1KLR7jcKaDDKDkvI9dP/FIhpmna5lkqPUQdEjFAM8=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.3.0 h1:VQbUHoJqytHHSJ1OZodPH9tvZZSVzUHjPHpkO85sT6k=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.3.0/go.mod h1:keUU7UfnwWTWpJ+FWnyqmogPa82nuU5VUANFq49hlMY=
go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q=
go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s=
go.opentelemetry.io/otel/sdk v1.3.0/go.mod h1:rIo4suHNhQwBIPg9axF8V9CA72Wz2mKF1teNrup8yzs=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0 h1:DheMAlT6POBP+gh8RUH19EOTnQIor5QE0uSRPtzCpSw=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0/go.mod h1:wZcGmeVO9nzP67aYSLDqXNWK87EZWhi7JWj1v7ZXf94=
go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U=
go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.20.0 h1:DeFD0VgTZ+Cj6hxravYYZE2W4GlneVH81iAOPjZkzk8=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.20.0/go.mod h1:GijYcYmNpX1KazD5JmWGsi4P7dDTTTnfv1UbGn84MnU=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.20.0 h1:gvmNvqrPYovvyRmCSygkUDyL8lC5Tl845MLEwqpxhEU=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.20.0/go.mod h1:vNUq47TGFioo+ffTSnKNdob241vePmtNZnAODKapKd0=
go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M=
go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8=
go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8=
go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E=
go.opentelemetry.io/otel/trace v1.3.0/go.mod h1:c/VDhno8888bvQYmbYLqe41/Ldmr/KKunbvWM4/fEjk=
go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g=
go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI=
go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM=
go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.opentelemetry.io/proto/otlp v0.11.0/go.mod h1:QpEjXPrNQzrFDZgoTo49dgHR9RYRSrg3NAKnUGl9YpQ=
go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJPI1Nnw=
go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA=
go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
@ -1409,8 +1424,10 @@ go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9E
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
go.uber.org/zap v1.17.0 h1:MTjgFu6ZLKvY6Pvaqk97GlxNBuMpV4Hy/3P6tRGlI2U=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY=
go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY=
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180723164146-c126467f60eb/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@ -1533,8 +1550,10 @@ golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
@ -1545,6 +1564,7 @@ golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su
golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220630215102-69896b714898/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
@ -1569,7 +1589,9 @@ golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210413134643-5e61552d6c78/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210615190721-d04028783cf1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
@ -1659,7 +1681,6 @@ golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210304124612-50617c2ba197/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -1680,9 +1701,11 @@ golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -1765,6 +1788,7 @@ golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@ -1806,6 +1830,7 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k=
@ -1927,6 +1952,7 @@ google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEY
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200806141610-86f49bd18e98/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
@ -1945,6 +1971,7 @@ google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQ
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
google.golang.org/genproto v0.0.0-20210617175327-b9e0b3197ced/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
@ -2080,6 +2107,7 @@ google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpX
google.golang.org/grpc v1.64.1 h1:LKtvyfbX3UGVPFcGqJ9ItpVWW6oN/2XqTxfAnwRRXiA=
google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
google.golang.org/grpc/examples v0.0.0-20210424002626-9572fd6faeae/go.mod h1:Ly7ZA/ARzg8fnPU9TyZIxoz33sEUuWX7txiqs8lPTgE=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20160105164936-4f90aeace3a2/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@ -2128,16 +2156,16 @@ lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl
modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI=
modernc.org/cc/v3 v3.36.2/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI=
modernc.org/cc/v3 v3.36.3/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI=
modernc.org/cc/v4 v4.20.0 h1:45Or8mQfbUqJOG9WaxvlFYOAQO0lQ5RvqBcFCXngjxk=
modernc.org/cc/v4 v4.20.0/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ=
modernc.org/cc/v4 v4.21.2 h1:dycHFB/jDc3IyacKipCNSDrjIC0Lm1hyoWOZTRR20Lk=
modernc.org/cc/v4 v4.21.2/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ=
modernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpNpA4bU1/i+sKOwOrQy8D87zWUcRZc=
modernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw=
modernc.org/ccgo/v3 v3.16.4/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ=
modernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ=
modernc.org/ccgo/v3 v3.16.8/go.mod h1:zNjwkizS+fIFDrDjIAgBSCLkWbJuHF+ar3QRn+Z9aws=
modernc.org/ccgo/v3 v3.16.9/go.mod h1:zNMzC9A9xeNUepy6KuZBbugn3c0Mc9TeiJO4lgvkJDo=
modernc.org/ccgo/v4 v4.16.0 h1:ofwORa6vx2FMm0916/CkZjpFPSR70VwTjUCe2Eg5BnA=
modernc.org/ccgo/v4 v4.16.0/go.mod h1:dkNyWIjFrVIZ68DTo36vHK+6/ShBn4ysU61So6PIqCI=
modernc.org/ccgo/v4 v4.17.8 h1:yyWBf2ipA0Y9GGz/MmCmi3EFpKgeS7ICrAFes+suEbs=
modernc.org/ccgo/v4 v4.17.8/go.mod h1:buJnJ6Fn0tyAdP/dqePbrrvLyr6qslFfTbFrCuaYvtA=
modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
@ -2153,8 +2181,8 @@ modernc.org/libc v1.16.17/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU=
modernc.org/libc v1.16.19/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA=
modernc.org/libc v1.17.0/go.mod h1:XsgLldpP4aWlPlsjqKRdHPqCxCjISdHfM/yeWC5GyW0=
modernc.org/libc v1.17.1/go.mod h1:FZ23b+8LjxZs7XtFMbSzL/EhPxNbfZbErxEHc7cbD9s=
modernc.org/libc v1.49.3 h1:j2MRCRdwJI2ls/sGbeSk0t2bypOG/uvPZUsGQFDulqg=
modernc.org/libc v1.49.3/go.mod h1:yMZuGkn7pXbKfoT/M35gFJOAEdSKdxL0q64sF7KqCDo=
modernc.org/libc v1.50.9 h1:hIWf1uz55lorXQhfoEoezdUHjxzuO6ceshET/yWjSjk=
modernc.org/libc v1.50.9/go.mod h1:15P6ublJ9FJR8YQCGy8DeQ2Uwur7iW9Hserr/T3OFZE=
modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
@ -2171,8 +2199,8 @@ modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc=
modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss=
modernc.org/sqlite v1.18.1/go.mod h1:6ho+Gow7oX5V+OiOQ6Tr4xeqbx13UZ6t+Fw9IRUG4d4=
modernc.org/sqlite v1.29.10 h1:3u93dz83myFnMilBGCOLbr+HjklS6+5rJLx4q86RDAg=
modernc.org/sqlite v1.29.10/go.mod h1:ItX2a1OVGgNsFh6Dv60JQvGfJfTPHPVpV6DF59akYOA=
modernc.org/sqlite v1.30.0 h1:8YhPUs/HTnlEgErn/jSYQTwHN/ex8CjHHjg+K9iG7LM=
modernc.org/sqlite v1.30.0/go.mod h1:cgkTARJ9ugeXSNaLBPK3CqbOe7Ec7ZhWPoMFGldEYEw=
modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw=
modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw=
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
@ -2182,6 +2210,8 @@ modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8=
pgregory.net/rapid v1.1.0 h1:CMa0sjHSru3puNx+J0MIAuiiEV4N0qj8/cMWGBBCsjw=
pgregory.net/rapid v1.1.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=

View file

@ -98,6 +98,18 @@ type OutRequest struct {
// the configured maximum payment timeout) the total time spent may be
// a multiple of this value.
PaymentTimeout time.Duration
// AssetId is an optional asset id that can be used to specify the asset
// that will be used to pay for the swap. If this is set, a connection
// to a tapd server is required to pay for the asset.
AssetId []byte
// AssetPrepayRfqId is the rfq id that is used to pay the prepay
// invoice.
AssetPrepayRfqId []byte
// AssetSwapRfqId is the rfq id that is used to pay the swap invoice.
AssetSwapRfqId []byte
}
// Out contains the full details of a loop out request. This includes things
@ -145,6 +157,25 @@ type LoopOutQuoteRequest struct {
// initiated the swap (loop CLI, autolooper, LiT UI and so on) and is
// appended to the user agent string.
Initiator string
// AssetRFQRequest is the optional RFQ request that can be used to quote
// for asset rfqs using the asset client
AssetRFQRequest *AssetRFQRequest
}
type AssetRFQRequest struct {
// AssetId is the asset that we'll quote for.
AssetId []byte
// AssetEdgeNode is the pubkey of the peer that we'll quote for.
AssetEdgeNode []byte
// Expiry is the unix timestamp when the rfq will expire.
Expiry int64
// MaxLimitMultiplier is the multiplier that we'll use to calculate the
// max limit we'll quote for.
MaxLimitMultiplier float64
}
// LoopOutTerms are the server terms on which it executes swaps.
@ -181,6 +212,31 @@ type LoopOutQuote struct {
// SwapPaymentDest is the node pubkey where to swap payment needs to be
// sent to.
SwapPaymentDest [33]byte
// LoopOutRfq is the RFQ that can be used in the actual loop out to
// commit to an asset exchange rate.
LoopOutRfq *LoopOutRfq
}
// LoopOutRfq contains the details of an asset request for quote for a loop out
// swap.
type LoopOutRfq struct {
// PrepayRfqId is the ID of the prepay RFQ.
PrepayRfqId []byte
// PrepayAssetAmt is the amount of the asset that will be used to pay
// for the prepay invoice.
PrepayAssetAmt uint64
// SwapRfqId is the ID of the swap RFQ.
SwapRfqId []byte
// SwapAssetAmt is the amount of the asset that will be used to pay for
// the swap invoice.
SwapAssetAmt uint64
// AssetName is the human readable name of the asset.
AssetName string
}
// LoopInRequest contains the required parameters for the swap.
@ -430,6 +486,9 @@ type SwapInfo struct {
// channels that may be used to loop out. On a loop in this field
// is nil.
OutgoingChanSet loopdb.ChannelSet
// AssetSwapInfo contains the asset information for the swap.
AssetSwapInfo *loopdb.LoopOutAssetSwap
}
// LastUpdate returns the last update time of the swap.

View file

@ -11,6 +11,7 @@ import (
"github.com/btcsuite/btcd/btcutil"
"github.com/lightninglabs/aperture/l402"
"github.com/lightninglabs/loop/assets"
"github.com/lightninglabs/loop/loopdb"
"github.com/lightningnetwork/lnd/cert"
"github.com/lightningnetwork/lnd/lncfg"
@ -196,6 +197,8 @@ type Config struct {
Server *loopServerConfig `group:"server" namespace:"server"`
Tapd *assets.TapdConfig `group:"tapd" namespace:"tapd"`
View viewParameters `command:"view" alias:"v" description:"View all swaps in the database. This command can only be executed when loopd is not running."`
}
@ -214,6 +217,7 @@ func DefaultConfig() Config {
Server: &loopServerConfig{
NoTLS: false,
},
Tapd: assets.DefaultTapdConfig(),
LoopDir: LoopDirBase,
ConfigFile: defaultConfigFile,
DataDir: LoopDirBase,

View file

@ -16,6 +16,7 @@ import (
proxy "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"github.com/lightninglabs/lndclient"
"github.com/lightninglabs/loop"
"github.com/lightninglabs/loop/assets"
"github.com/lightninglabs/loop/instantout"
"github.com/lightninglabs/loop/instantout/reservation"
"github.com/lightninglabs/loop/loopd/perms"
@ -28,6 +29,7 @@ import (
"github.com/lightninglabs/loop/staticaddr/withdraw"
loop_swaprpc "github.com/lightninglabs/loop/swapserverrpc"
"github.com/lightninglabs/loop/sweepbatcher"
"github.com/lightninglabs/taproot-assets/taprpc"
"github.com/lightningnetwork/lnd/clock"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/macaroons"
@ -82,6 +84,7 @@ type Daemon struct {
internalErrChan chan error
lnd *lndclient.GrpcLndServices
assetClient *assets.TapdClient
clientCleanup func()
wg sync.WaitGroup
@ -138,6 +141,14 @@ func (d *Daemon) Start() error {
return err
}
// Initialize the assets client.
if d.cfg.Tapd.Activate {
d.assetClient, err = assets.NewTapdClient(d.cfg.Tapd)
if err != nil {
return err
}
}
// With lnd connected, initialize everything else, such as the swap
// server client, the swap client RPC server instance and our main swap
// and error handlers. If this fails, then nothing has been started yet,
@ -435,9 +446,26 @@ func (d *Daemon) initialize(withMacaroonService bool) error {
chainParams,
)
// If we're running an asset client, we'll log something here.
if d.assetClient != nil {
getInfo, err := d.assetClient.GetInfo(
d.mainCtx, &taprpc.GetInfoRequest{},
)
if err != nil {
return fmt.Errorf("unable to get asset client info: %v", err)
}
if getInfo.LndIdentityPubkey != d.lnd.NodePubkey.String() {
return fmt.Errorf("asset client pubkey %v does not match "+
"lnd pubkey %v", getInfo.LndIdentityPubkey,
d.lnd.NodePubkey)
}
log.Infof("Using asset client with version %v", getInfo.Version)
}
// Create an instance of the loop client library.
swapClient, clientCleanup, err := getClient(
d.cfg, swapDb, sweeperDb, &d.lnd.LndServices,
d.cfg, swapDb, sweeperDb, &d.lnd.LndServices, d.assetClient,
)
if err != nil {
return err
@ -667,6 +695,7 @@ func (d *Daemon) initialize(withMacaroonService bool) error {
depositManager: depositManager,
withdrawalManager: withdrawalManager,
staticLoopInManager: staticLoopInManager,
assetClient: d.assetClient,
}
// Retrieve all currently existing swaps from the database.

View file

@ -19,6 +19,7 @@ import (
"github.com/lightninglabs/aperture/l402"
"github.com/lightninglabs/lndclient"
"github.com/lightninglabs/loop"
"github.com/lightninglabs/loop/assets"
"github.com/lightninglabs/loop/fsm"
"github.com/lightninglabs/loop/instantout"
"github.com/lightninglabs/loop/instantout/reservation"
@ -93,6 +94,7 @@ type swapClientServer struct {
depositManager *deposit.Manager
withdrawalManager *withdraw.Manager
staticLoopInManager *loopin.Manager
assetClient *assets.TapdClient
swaps map[lntypes.Hash]loop.SwapInfo
subscribers map[int]chan<- interface{}
statusChan chan loop.SwapInfo
@ -208,6 +210,38 @@ func (s *swapClientServer) LoopOut(ctx context.Context,
PaymentTimeout: paymentTimeout,
}
// If the asset id is set, we need to set the asset amount and asset id
// in the request.
if in.AssetInfo != nil {
if len(in.AssetInfo.AssetId) != 0 &&
len(in.AssetInfo.AssetId) != 32 {
return nil, fmt.Errorf(
"asset id must be set to a 32 byte value",
)
}
if len(in.AssetRfqInfo.PrepayRfqId) != 0 &&
len(in.AssetRfqInfo.PrepayRfqId) != 32 {
return nil, fmt.Errorf(
"prepay rfq id must be set to a 32 byte value",
)
}
if len(in.AssetRfqInfo.SwapRfqId) != 0 &&
len(in.AssetRfqInfo.SwapRfqId) != 32 {
return nil, fmt.Errorf(
"swap rfq id must be set to a 32 byte value",
)
}
req.AssetId = in.AssetInfo.AssetId
req.AssetPrepayRfqId = in.AssetRfqInfo.PrepayRfqId
req.AssetSwapRfqId = in.AssetRfqInfo.SwapRfqId
}
switch {
case in.LoopOutChannel != 0 && len(in.OutgoingChanSet) > 0: // nolint:staticcheck
return nil, errors.New("loop_out_channel and outgoing_" +
@ -275,8 +309,8 @@ func toWalletAddrType(addrType looprpc.AddressType) (walletrpc.AddressType,
}
}
func (s *swapClientServer) marshallSwap(loopSwap *loop.SwapInfo) (
*looprpc.SwapStatus, error) {
func (s *swapClientServer) marshallSwap(ctx context.Context,
loopSwap *loop.SwapInfo) (*looprpc.SwapStatus, error) {
var (
state looprpc.SwapState
@ -349,6 +383,7 @@ func (s *swapClientServer) marshallSwap(loopSwap *loop.SwapInfo) (
)
var outGoingChanSet []uint64
var lastHop []byte
var assetInfo *looprpc.AssetLoopOutInfo
switch loopSwap.SwapType {
case swap.TypeIn:
@ -379,6 +414,22 @@ func (s *swapClientServer) marshallSwap(loopSwap *loop.SwapInfo) (
outGoingChanSet = loopSwap.OutgoingChanSet
if loopSwap.AssetSwapInfo != nil {
assetName, err := s.assetClient.GetAssetName(
ctx, loopSwap.AssetSwapInfo.AssetId,
)
if err != nil {
return nil, err
}
assetInfo = &looprpc.AssetLoopOutInfo{
AssetId: hex.EncodeToString(loopSwap.AssetSwapInfo.AssetId), // nolint:lll
AssetCostOffchain: loopSwap.AssetSwapInfo.PrepayPaidAmt +
loopSwap.AssetSwapInfo.SwapPaidAmt, // nolint:lll
AssetName: assetName,
}
}
default:
return nil, errors.New("unknown swap type")
}
@ -401,6 +452,7 @@ func (s *swapClientServer) marshallSwap(loopSwap *loop.SwapInfo) (
Label: loopSwap.Label,
LastHop: lastHop,
OutgoingChanSet: outGoingChanSet,
AssetInfo: assetInfo,
}, nil
}
@ -411,7 +463,7 @@ func (s *swapClientServer) Monitor(in *looprpc.MonitorRequest,
log.Infof("Monitor request received")
send := func(info loop.SwapInfo) error {
rpcSwap, err := s.marshallSwap(&info)
rpcSwap, err := s.marshallSwap(server.Context(), &info)
if err != nil {
return err
}
@ -506,7 +558,7 @@ func (s *swapClientServer) Monitor(in *looprpc.MonitorRequest,
// ListSwaps returns a list of all currently known swaps and their current
// status.
func (s *swapClientServer) ListSwaps(_ context.Context,
func (s *swapClientServer) ListSwaps(ctx context.Context,
req *looprpc.ListSwapsRequest) (*looprpc.ListSwapsResponse, error) {
var (
@ -529,7 +581,7 @@ func (s *swapClientServer) ListSwaps(_ context.Context,
continue
}
rpcSwap, err := s.marshallSwap(&swp)
rpcSwap, err := s.marshallSwap(ctx, &swp)
if err != nil {
return nil, err
}
@ -607,11 +659,17 @@ func filterSwap(swapInfo *loop.SwapInfo, filter *looprpc.ListSwapsFilter) bool {
}
}
// If we only want to return asset swaps, we only return swaps that have
// an asset id set.
if filter.AssetSwapOnly && swapInfo.AssetSwapInfo == nil {
return false
}
return true
}
// SwapInfo returns all known details about a single swap.
func (s *swapClientServer) SwapInfo(_ context.Context,
func (s *swapClientServer) SwapInfo(ctx context.Context,
req *looprpc.SwapInfoRequest) (*looprpc.SwapStatus, error) {
swapHash, err := lntypes.MakeHash(req.Id)
@ -625,7 +683,7 @@ func (s *swapClientServer) SwapInfo(_ context.Context,
if !ok {
return nil, fmt.Errorf("swap with hash %s not found", req.Id)
}
return s.marshallSwap(&swp)
return s.marshallSwap(ctx, &swp)
}
// AbandonSwap requests the server to abandon a swap with the given hash.
@ -707,23 +765,52 @@ func (s *swapClientServer) LoopOutQuote(ctx context.Context,
req.SwapPublicationDeadline,
)
quote, err := s.impl.LoopOutQuote(ctx, &loop.LoopOutQuoteRequest{
loopOutQuoteReq := &loop.LoopOutQuoteRequest{
Amount: btcutil.Amount(req.Amt),
SweepConfTarget: confTarget,
SwapPublicationDeadline: publicactionDeadline,
Initiator: defaultLoopdInitiator,
})
}
if req.AssetInfo != nil {
if req.AssetInfo.AssetId == nil ||
req.AssetInfo.AssetEdgeNode == nil {
return nil, fmt.Errorf(
"asset id and edge node must both be set")
}
loopOutQuoteReq.AssetRFQRequest = &loop.AssetRFQRequest{
AssetId: req.AssetInfo.AssetId,
AssetEdgeNode: req.AssetInfo.AssetEdgeNode,
Expiry: req.AssetInfo.Expiry,
MaxLimitMultiplier: req.AssetInfo.MaxLimitMultiplier,
}
}
quote, err := s.impl.LoopOutQuote(ctx, loopOutQuoteReq)
if err != nil {
return nil, err
}
return &looprpc.OutQuoteResponse{
response := &looprpc.OutQuoteResponse{
HtlcSweepFeeSat: int64(quote.MinerFee),
PrepayAmtSat: int64(quote.PrepayAmount),
SwapFeeSat: int64(quote.SwapFee),
SwapPaymentDest: quote.SwapPaymentDest[:],
ConfTarget: confTarget,
}, nil
}
if quote.LoopOutRfq != nil {
response.AssetRfqInfo = &looprpc.AssetRfqInfo{
PrepayRfqId: quote.LoopOutRfq.PrepayRfqId,
PrepayAssetAmt: quote.LoopOutRfq.PrepayAssetAmt,
SwapRfqId: quote.LoopOutRfq.SwapRfqId,
SwapAssetAmt: quote.LoopOutRfq.SwapAssetAmt,
AssetName: quote.LoopOutRfq.AssetName,
}
}
return response, nil
}
// GetLoopInTerms returns the terms that the server enforces for swaps.
@ -2023,6 +2110,15 @@ func validateLoopOutRequest(ctx context.Context, lnd lndclient.LightningClient,
return 0, errInvalidAddress
}
// If this is an asset payment, we'll check that we have the necessary
// outbound asset capacaity to fulfill the request.
if req.AssetInfo != nil {
// Todo(sputn1ck) actually check outbound capacity.
return validateConfTarget(
req.SweepConfTarget, loop.DefaultSweepConfTarget,
)
}
// Check that the label is valid.
if err := labels.Validate(req.Label); err != nil {
return 0, err

View file

@ -9,6 +9,7 @@ import (
"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/swap"
@ -19,8 +20,8 @@ import (
// getClient returns an instance of the swap client.
func getClient(cfg *Config, swapDb loopdb.SwapStore,
sweeperDb sweepbatcher.BatcherStore, lnd *lndclient.LndServices) (
*loop.Client, func(), error) {
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.
@ -45,6 +46,7 @@ func getClient(cfg *Config, swapDb loopdb.SwapStore,
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,

View file

@ -7,6 +7,7 @@ import (
"github.com/btcsuite/btcd/chaincfg"
"github.com/lightninglabs/lndclient"
"github.com/lightninglabs/loop"
"github.com/lightninglabs/loop/assets"
"github.com/lightninglabs/loop/loopdb"
"github.com/lightninglabs/loop/sweepbatcher"
"github.com/lightninglabs/loop/utils"
@ -37,8 +38,16 @@ func view(config *Config, lisCfg *ListenerCfg) error {
chainParams,
)
var assetClient *assets.TapdClient
if config.Tapd.Host != "" {
assetClient, err = assets.NewTapdClient(config.Tapd)
if err != nil {
return err
}
}
swapClient, cleanup, err := getClient(
config, swapDb, sweeperDb, &lnd.LndServices,
config, swapDb, sweeperDb, &lnd.LndServices, assetClient,
)
if err != nil {
return err

View file

@ -30,6 +30,11 @@ type SwapStore interface {
UpdateLoopOut(ctx context.Context, hash lntypes.Hash, time time.Time,
state SwapStateData) error
// UpdateLoopOutAssetInfo updates the asset information for a loop out
// swap.
UpdateLoopOutAssetInfo(ctx context.Context, hash lntypes.Hash,
asset *LoopOutAssetSwap) error
// FetchLoopInSwaps returns all swaps currently in the store.
FetchLoopInSwaps(ctx context.Context) ([]*LoopIn, error)

View file

@ -65,6 +65,28 @@ type LoopOutContract struct {
// PaymentTimeout is the timeout for any individual off-chain payment
// attempt.
PaymentTimeout time.Duration
// AssetSwapInfo contains information, should the loop out swpa be
// paid via an asset channel.
AssetSwapInfo *LoopOutAssetSwap
}
type LoopOutAssetSwap struct {
// AssetId is the optional asset id that is used to pay the swap invoice.
AssetId []byte
// PrepayRfqId is the rfq id that is used to pay the prepay invoice.
PrepayRfqId []byte
// SwapRfqId is the rfq id that is used to pay the swap invoice.
SwapRfqId []byte
// PrepayPaidAmt is the asset amount that was paid for the prepay
// invoice.
PrepayPaidAmt uint64
// SwapPaidAmt is the asset amount that was paid for the swap invoice.
SwapPaidAmt uint64
}
// ChannelSet stores a set of channels.

View file

@ -126,10 +126,40 @@ func (db *BaseDB) CreateLoopOut(ctx context.Context, hash lntypes.Hash,
return err
}
// If the loop is an asset loop out, we'll also insert the
// asset details.
if swap.AssetSwapInfo != nil {
assetInfo := swap.AssetSwapInfo
err = tx.InsertLoopOutAsset(
ctx, sqlc.InsertLoopOutAssetParams{
SwapHash: hash[:],
AssetID: assetInfo.AssetId,
SwapRfqID: assetInfo.SwapRfqId,
PrepayRfqID: assetInfo.PrepayRfqId,
},
)
if err != nil {
return err
}
}
return nil
})
}
// UpdateLoopOutAssetInfo updates the offchain send amounts of the prepay and
// swap payment for an asset loop out swap.
func (db *BaseDB) UpdateLoopOutAssetInfo(ctx context.Context, hash lntypes.Hash,
asset *LoopOutAssetSwap) error {
return db.UpdateLoopOutAssetOffchainPayments(
ctx, sqlc.UpdateLoopOutAssetOffchainPaymentsParams{
SwapHash: hash[:],
AssetAmtPaidSwap: int64(asset.SwapPaidAmt),
AssetAmtPaidPrepay: int64(asset.PrepayPaidAmt),
})
}
// BatchCreateLoopOut adds multiple initiated swaps to the store.
func (db *BaseDB) BatchCreateLoopOut(ctx context.Context,
swaps map[lntypes.Hash]*LoopOutContract) error {
@ -543,7 +573,8 @@ func swapToHtlcKeysInsertArgs(hash lntypes.Hash,
// ConvertLoopOutRow converts a database row containing a loop out swap to a
// LoopOut struct.
func ConvertLoopOutRow(network *chaincfg.Params, row sqlc.GetLoopOutSwapRow,
updates []sqlc.SwapUpdate) (*LoopOut, error) {
updates []sqlc.SwapUpdate) (*LoopOut,
error) {
htlcKeys, err := fetchHtlcKeys(
row.SenderScriptPubkey, row.ReceiverScriptPubkey,
@ -601,6 +632,20 @@ func ConvertLoopOutRow(network *chaincfg.Params, row sqlc.GetLoopOutSwapRow,
},
}
if row.AssetID != nil {
loopOut.Contract.AssetSwapInfo = &LoopOutAssetSwap{
AssetId: row.AssetID,
SwapRfqId: row.SwapRfqID,
PrepayRfqId: row.PrepayRfqID,
SwapPaidAmt: uint64(
unmarshalSqlInt64(row.AssetAmtPaidSwap),
),
PrepayPaidAmt: uint64(
unmarshalSqlInt64(row.AssetAmtPaidPrepay),
),
}
}
if row.OutgoingChanSet != "" {
chanSet, err := ConvertOutgoingChanSet(row.OutgoingChanSet)
if err != nil {
@ -803,3 +848,11 @@ func blobTo33ByteSlice(blob []byte) ([33]byte, error) {
return key, nil
}
func unmarshalSqlInt64(data sql.NullInt64) int64 {
if !data.Valid {
return 0
}
return data.Int64
}

View file

@ -22,6 +22,15 @@ const (
testLabel = "test label"
)
var (
testAssetId = []byte{
1, 1, 1, 1, 2, 2, 2, 2,
3, 3, 3, 3, 4, 4, 4, 4,
1, 1, 1, 1, 2, 2, 2, 2,
3, 3, 3, 3, 4, 4, 4, 4,
}
)
// TestSqliteLoopOutStore tests all the basic functionality of the current
// sqlite swap store.
func TestSqliteLoopOutStore(t *testing.T) {
@ -80,6 +89,17 @@ func TestSqliteLoopOutStore(t *testing.T) {
t.Run("labelled swap", func(t *testing.T) {
testSqliteLoopOutStore(t, &labelledSwap)
})
assetSwap := unrestrictedSwap
assetSwap.AssetSwapInfo = &LoopOutAssetSwap{
AssetId: testAssetId,
PrepayRfqId: testAssetId,
SwapRfqId: testAssetId,
}
t.Run("asset swap", func(t *testing.T) {
testSqliteLoopOutStore(t, &assetSwap)
})
}
// testSqliteLoopOutStore tests the basic functionality of the current sqlite

View file

@ -0,0 +1 @@
DROP TABLE IF EXISTS loopout_swaps_assets;

View file

@ -0,0 +1,22 @@
CREATE TABLE IF NOT EXISTS loopout_swaps_asset_info (
-- swap_hash points to the parent loop out swap hash.
swap_hash BLOB PRIMARY KEY REFERENCES loopout_swaps(swap_hash),
-- asset_id is the asset that is used to pay the swap invoice.
asset_id BYTEA NOT NULL,
-- swap_rfq_id is the RFQ id that will be used to pay the swap invoice.
swap_rfq_id BYTEA NOT NULL,
-- prepay_rfq_id is the RFQ id that will be used to pay the prepay
-- invoice.
prepay_rfq_id BYTEA NOT NULL,
-- asset_amt_paid_swap is the actual asset amt that has been paid for
-- the swap invoice.
asset_amt_paid_swap BIGINT NOT NULL DEFAULT 0,
-- asset_amt_paid_prepay is the actual asset amt that has been paid for
-- the prepay invoice.
asset_amt_paid_prepay BIGINT NOT NULL DEFAULT 0
)

View file

@ -86,6 +86,15 @@ type LoopoutSwap struct {
PaymentTimeout int32
}
type LoopoutSwapsAssetInfo struct {
SwapHash []byte
AssetID []byte
SwapRfqID []byte
PrepayRfqID []byte
AssetAmtPaidSwap int64
AssetAmtPaidPrepay int64
}
type MigrationTracker struct {
MigrationID string
MigrationTs sql.NullTime

View file

@ -49,6 +49,7 @@ type Querier interface {
InsertInstantOutUpdate(ctx context.Context, arg InsertInstantOutUpdateParams) error
InsertLoopIn(ctx context.Context, arg InsertLoopInParams) error
InsertLoopOut(ctx context.Context, arg InsertLoopOutParams) error
InsertLoopOutAsset(ctx context.Context, arg InsertLoopOutAssetParams) error
InsertMigration(ctx context.Context, arg InsertMigrationParams) error
InsertReservationUpdate(ctx context.Context, arg InsertReservationUpdateParams) error
InsertStaticAddressLoopIn(ctx context.Context, arg InsertStaticAddressLoopInParams) error
@ -60,6 +61,7 @@ type Querier interface {
UpdateBatch(ctx context.Context, arg UpdateBatchParams) error
UpdateDeposit(ctx context.Context, arg UpdateDepositParams) error
UpdateInstantOut(ctx context.Context, arg UpdateInstantOutParams) error
UpdateLoopOutAssetOffchainPayments(ctx context.Context, arg UpdateLoopOutAssetOffchainPaymentsParams) error
UpdateReservation(ctx context.Context, arg UpdateReservationParams) error
UpdateStaticAddressLoopIn(ctx context.Context, arg UpdateStaticAddressLoopInParams) error
UpsertLiquidityParams(ctx context.Context, params []byte) error

View file

@ -2,13 +2,16 @@
SELECT
swaps.*,
loopout_swaps.*,
htlc_keys.*
htlc_keys.*,
loopout_swaps_asset_info.*
FROM
swaps
JOIN
loopout_swaps ON swaps.swap_hash = loopout_swaps.swap_hash
JOIN
htlc_keys ON swaps.swap_hash = htlc_keys.swap_hash
LEFT JOIN
loopout_swaps_asset_info ON swaps.swap_hash = loopout_swaps_asset_info.swap_hash
ORDER BY
swaps.id;
@ -16,13 +19,16 @@ ORDER BY
SELECT
swaps.*,
loopout_swaps.*,
htlc_keys.*
htlc_keys.*,
loopout_swaps_asset_info.*
FROM
swaps
JOIN
loopout_swaps ON swaps.swap_hash = loopout_swaps.swap_hash
JOIN
htlc_keys ON swaps.swap_hash = htlc_keys.swap_hash
LEFT JOIN
loopout_swaps_asset_info ON swaps.swap_hash = loopout_swaps_asset_info.swap_hash
WHERE
swaps.swap_hash = $1;
@ -111,6 +117,23 @@ INSERT INTO loopout_swaps (
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12
);
-- name: InsertLoopOutAsset :exec
INSERT INTO loopout_swaps_asset_info (
swap_hash,
asset_id,
swap_rfq_id,
prepay_rfq_id
) VALUES (
$1, $2, $3, $4
);
-- name: UpdateLoopOutAssetOffchainPayments :exec
UPDATE loopout_swaps_asset_info
SET
asset_amt_paid_swap = $2,
asset_amt_paid_prepay = $3
WHERE swap_hash = $1;
-- name: InsertLoopIn :exec
INSERT INTO loopin_swaps (
swap_hash,

View file

@ -7,6 +7,7 @@ package sqlc
import (
"context"
"database/sql"
"time"
)
@ -185,13 +186,16 @@ const getLoopOutSwap = `-- name: GetLoopOutSwap :one
SELECT
swaps.id, swaps.swap_hash, swaps.preimage, swaps.initiation_time, swaps.amount_requested, swaps.cltv_expiry, swaps.max_miner_fee, swaps.max_swap_fee, swaps.initiation_height, swaps.protocol_version, swaps.label,
loopout_swaps.swap_hash, loopout_swaps.dest_address, loopout_swaps.swap_invoice, loopout_swaps.max_swap_routing_fee, loopout_swaps.sweep_conf_target, loopout_swaps.htlc_confirmations, loopout_swaps.outgoing_chan_set, loopout_swaps.prepay_invoice, loopout_swaps.max_prepay_routing_fee, loopout_swaps.publication_deadline, loopout_swaps.single_sweep, loopout_swaps.payment_timeout,
htlc_keys.swap_hash, htlc_keys.sender_script_pubkey, htlc_keys.receiver_script_pubkey, htlc_keys.sender_internal_pubkey, htlc_keys.receiver_internal_pubkey, htlc_keys.client_key_family, htlc_keys.client_key_index
htlc_keys.swap_hash, htlc_keys.sender_script_pubkey, htlc_keys.receiver_script_pubkey, htlc_keys.sender_internal_pubkey, htlc_keys.receiver_internal_pubkey, htlc_keys.client_key_family, htlc_keys.client_key_index,
loopout_swaps_asset_info.swap_hash, loopout_swaps_asset_info.asset_id, loopout_swaps_asset_info.swap_rfq_id, loopout_swaps_asset_info.prepay_rfq_id, loopout_swaps_asset_info.asset_amt_paid_swap, loopout_swaps_asset_info.asset_amt_paid_prepay
FROM
swaps
JOIN
loopout_swaps ON swaps.swap_hash = loopout_swaps.swap_hash
JOIN
htlc_keys ON swaps.swap_hash = htlc_keys.swap_hash
LEFT JOIN
loopout_swaps_asset_info ON swaps.swap_hash = loopout_swaps_asset_info.swap_hash
WHERE
swaps.swap_hash = $1
`
@ -227,6 +231,12 @@ type GetLoopOutSwapRow struct {
ReceiverInternalPubkey []byte
ClientKeyFamily int32
ClientKeyIndex int32
SwapHash_4 []byte
AssetID []byte
SwapRfqID []byte
PrepayRfqID []byte
AssetAmtPaidSwap sql.NullInt64
AssetAmtPaidPrepay sql.NullInt64
}
func (q *Queries) GetLoopOutSwap(ctx context.Context, swapHash []byte) (GetLoopOutSwapRow, error) {
@ -263,6 +273,12 @@ func (q *Queries) GetLoopOutSwap(ctx context.Context, swapHash []byte) (GetLoopO
&i.ReceiverInternalPubkey,
&i.ClientKeyFamily,
&i.ClientKeyIndex,
&i.SwapHash_4,
&i.AssetID,
&i.SwapRfqID,
&i.PrepayRfqID,
&i.AssetAmtPaidSwap,
&i.AssetAmtPaidPrepay,
)
return i, err
}
@ -271,13 +287,16 @@ const getLoopOutSwaps = `-- name: GetLoopOutSwaps :many
SELECT
swaps.id, swaps.swap_hash, swaps.preimage, swaps.initiation_time, swaps.amount_requested, swaps.cltv_expiry, swaps.max_miner_fee, swaps.max_swap_fee, swaps.initiation_height, swaps.protocol_version, swaps.label,
loopout_swaps.swap_hash, loopout_swaps.dest_address, loopout_swaps.swap_invoice, loopout_swaps.max_swap_routing_fee, loopout_swaps.sweep_conf_target, loopout_swaps.htlc_confirmations, loopout_swaps.outgoing_chan_set, loopout_swaps.prepay_invoice, loopout_swaps.max_prepay_routing_fee, loopout_swaps.publication_deadline, loopout_swaps.single_sweep, loopout_swaps.payment_timeout,
htlc_keys.swap_hash, htlc_keys.sender_script_pubkey, htlc_keys.receiver_script_pubkey, htlc_keys.sender_internal_pubkey, htlc_keys.receiver_internal_pubkey, htlc_keys.client_key_family, htlc_keys.client_key_index
htlc_keys.swap_hash, htlc_keys.sender_script_pubkey, htlc_keys.receiver_script_pubkey, htlc_keys.sender_internal_pubkey, htlc_keys.receiver_internal_pubkey, htlc_keys.client_key_family, htlc_keys.client_key_index,
loopout_swaps_asset_info.swap_hash, loopout_swaps_asset_info.asset_id, loopout_swaps_asset_info.swap_rfq_id, loopout_swaps_asset_info.prepay_rfq_id, loopout_swaps_asset_info.asset_amt_paid_swap, loopout_swaps_asset_info.asset_amt_paid_prepay
FROM
swaps
JOIN
loopout_swaps ON swaps.swap_hash = loopout_swaps.swap_hash
JOIN
htlc_keys ON swaps.swap_hash = htlc_keys.swap_hash
LEFT JOIN
loopout_swaps_asset_info ON swaps.swap_hash = loopout_swaps_asset_info.swap_hash
ORDER BY
swaps.id
`
@ -313,6 +332,12 @@ type GetLoopOutSwapsRow struct {
ReceiverInternalPubkey []byte
ClientKeyFamily int32
ClientKeyIndex int32
SwapHash_4 []byte
AssetID []byte
SwapRfqID []byte
PrepayRfqID []byte
AssetAmtPaidSwap sql.NullInt64
AssetAmtPaidPrepay sql.NullInt64
}
func (q *Queries) GetLoopOutSwaps(ctx context.Context) ([]GetLoopOutSwapsRow, error) {
@ -355,6 +380,12 @@ func (q *Queries) GetLoopOutSwaps(ctx context.Context) ([]GetLoopOutSwapsRow, er
&i.ReceiverInternalPubkey,
&i.ClientKeyFamily,
&i.ClientKeyIndex,
&i.SwapHash_4,
&i.AssetID,
&i.SwapRfqID,
&i.PrepayRfqID,
&i.AssetAmtPaidSwap,
&i.AssetAmtPaidPrepay,
); err != nil {
return nil, err
}
@ -529,6 +560,34 @@ func (q *Queries) InsertLoopOut(ctx context.Context, arg InsertLoopOutParams) er
return err
}
const insertLoopOutAsset = `-- name: InsertLoopOutAsset :exec
INSERT INTO loopout_swaps_asset_info (
swap_hash,
asset_id,
swap_rfq_id,
prepay_rfq_id
) VALUES (
$1, $2, $3, $4
)
`
type InsertLoopOutAssetParams struct {
SwapHash []byte
AssetID []byte
SwapRfqID []byte
PrepayRfqID []byte
}
func (q *Queries) InsertLoopOutAsset(ctx context.Context, arg InsertLoopOutAssetParams) error {
_, err := q.db.ExecContext(ctx, insertLoopOutAsset,
arg.SwapHash,
arg.AssetID,
arg.SwapRfqID,
arg.PrepayRfqID,
)
return err
}
const insertSwap = `-- name: InsertSwap :exec
INSERT INTO swaps (
swap_hash,
@ -637,3 +696,22 @@ func (q *Queries) OverrideSwapCosts(ctx context.Context, arg OverrideSwapCostsPa
)
return err
}
const updateLoopOutAssetOffchainPayments = `-- name: UpdateLoopOutAssetOffchainPayments :exec
UPDATE loopout_swaps_asset_info
SET
asset_amt_paid_swap = $2,
asset_amt_paid_prepay = $3
WHERE swap_hash = $1
`
type UpdateLoopOutAssetOffchainPaymentsParams struct {
SwapHash []byte
AssetAmtPaidSwap int64
AssetAmtPaidPrepay int64
}
func (q *Queries) UpdateLoopOutAssetOffchainPayments(ctx context.Context, arg UpdateLoopOutAssetOffchainPaymentsParams) error {
_, err := q.db.ExecContext(ctx, updateLoopOutAssetOffchainPayments, arg.SwapHash, arg.AssetAmtPaidSwap, arg.AssetAmtPaidPrepay)
return err
}

View file

@ -568,6 +568,13 @@ func (s *boltSwapStore) CreateLoopOut(ctx context.Context, hash lntypes.Hash,
})
}
// UpdateLoopOutAssetInfo is unused for the bolt swap store.
func (db *boltSwapStore) UpdateLoopOutAssetInfo(ctx context.Context, hash lntypes.Hash,
asset *LoopOutAssetSwap) error {
return errors.New("unimplemented")
}
// CreateLoopIn adds an initiated swap to the store.
//
// NOTE: Part of the loopdb.SwapStore interface.

View file

@ -165,6 +165,12 @@ func (s *StoreMock) FetchLoopInSwaps(ctx context.Context) ([]*LoopIn,
return result, nil
}
func (s *StoreMock) UpdateLoopOutAssetInfo(ctx context.Context,
hash lntypes.Hash, asset *LoopOutAssetSwap) error {
return errors.New("not implemented")
}
// CreateLoopIn adds an initiated loop in swap to the store.
//
// NOTE: Part of the SwapStore interface.

View file

@ -177,6 +177,7 @@ func newLoopInSwap(globalCtx context.Context, cfg *swapConfig,
Memo: "swap",
Expiry: 3600 * 24 * 365,
RouteHints: request.RouteHints,
Private: true,
},
)
if err != nil {
@ -197,6 +198,7 @@ func newLoopInSwap(globalCtx context.Context, cfg *swapConfig,
Memo: "loop in probe",
Expiry: 3600,
RouteHints: request.RouteHints,
Private: true,
},
)
if err != nil {

View file

@ -47,7 +47,7 @@ func testLoopInSuccess(t *testing.T) {
height := int32(600)
cfg := newSwapConfig(&ctx.lnd.LndServices, ctx.store, ctx.server)
cfg := newSwapConfig(&ctx.lnd.LndServices, ctx.store, ctx.server, nil)
expectedLastHop := &route.Vertex{0x02}
@ -200,7 +200,7 @@ func testLoopInTimeout(t *testing.T, externalValue int64) {
height := int32(600)
cfg := newSwapConfig(&ctx.lnd.LndServices, ctx.store, ctx.server)
cfg := newSwapConfig(&ctx.lnd.LndServices, ctx.store, ctx.server, nil)
req := testLoopInRequest
if externalValue != 0 {
@ -414,7 +414,7 @@ func testLoopInResume(t *testing.T, state loopdb.SwapState, expired bool,
ctxb := context.Background()
ctx := newLoopInTestContext(t)
cfg := newSwapConfig(&ctx.lnd.LndServices, ctx.store, ctx.server)
cfg := newSwapConfig(&ctx.lnd.LndServices, ctx.store, ctx.server, nil)
// Create sender and receiver keys.
_, senderPubKey := test.CreateKey(1)
@ -769,7 +769,7 @@ func advanceToPublishedHtlc(t *testing.T, ctx *loopInTestContext) SwapInfo {
func startNewLoopIn(t *testing.T, ctx *loopInTestContext, height int32) (
*swapConfig, error, *loopInSwap) {
cfg := newSwapConfig(&ctx.lnd.LndServices, ctx.store, ctx.server)
cfg := newSwapConfig(&ctx.lnd.LndServices, ctx.store, ctx.server, nil)
req := &testLoopInRequest

View file

@ -4,6 +4,7 @@ import (
"context"
"crypto/rand"
"crypto/sha256"
"encoding/json"
"errors"
"fmt"
"math"
@ -20,10 +21,13 @@ import (
"github.com/lightninglabs/loop/sweep"
"github.com/lightninglabs/loop/sweepbatcher"
"github.com/lightninglabs/loop/utils"
"github.com/lightninglabs/taproot-assets/fn"
"github.com/lightninglabs/taproot-assets/rfqmsg"
"github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/tlv"
)
const (
@ -140,6 +144,18 @@ func newLoopOutSwap(globalCtx context.Context, cfg *swapConfig,
log.Infof("Initiating swap request at height %v: amt=%v, expiry=%v",
currentHeight, request.Amount, request.Expiry)
// If we have an asset id, we'll add that to the user agent.
if request.AssetId != nil {
if request.AssetPrepayRfqId == nil ||
request.AssetSwapRfqId == nil {
return nil, errors.New("both rfq ids must be set for " +
"asset swaps")
}
request.Initiator += " asset_out"
}
// The swap deadline will be given to the server for it to use as the
// latest swap publication time.
swapResp, err := cfg.server.NewLoopOutSwap(
@ -208,6 +224,14 @@ func newLoopOutSwap(globalCtx context.Context, cfg *swapConfig,
PaymentTimeout: request.PaymentTimeout,
}
if request.AssetId != nil {
contract.AssetSwapInfo = &loopdb.LoopOutAssetSwap{
AssetId: request.AssetId,
PrepayRfqId: request.AssetPrepayRfqId,
SwapRfqId: request.AssetSwapRfqId,
}
}
swapKit := newSwapKit(
swapHash, swap.TypeOut, cfg, &contract.SwapContract,
)
@ -332,6 +356,10 @@ func (s *loopOutSwap) sendUpdate(ctx context.Context) error {
info.OutgoingChanSet = outgoingChanSet
}
if s.isAssetSwap() {
info.AssetSwapInfo = s.AssetSwapInfo
}
select {
case s.statusChan <- *info:
case <-ctx.Done():
@ -413,7 +441,7 @@ func (s *loopOutSwap) executeAndFinalize(globalCtx context.Context) error {
case result := <-s.swapPaymentChan:
s.swapPaymentChan = nil
err := s.handlePaymentResult(result, true)
err := s.handlePaymentResult(globalCtx, result, true)
if err != nil {
return err
}
@ -429,7 +457,7 @@ func (s *loopOutSwap) executeAndFinalize(globalCtx context.Context) error {
case result := <-s.prePaymentChan:
s.prePaymentChan = nil
err := s.handlePaymentResult(result, false)
err := s.handlePaymentResult(globalCtx, result, false)
if err != nil {
return err
}
@ -462,8 +490,8 @@ func (s *loopOutSwap) executeAndFinalize(globalCtx context.Context) error {
// handlePaymentResult processes the result of a payment attempt. If the
// payment was successful and this is the main swap payment, the cost of the
// swap is updated.
func (s *loopOutSwap) handlePaymentResult(result paymentResult,
swapPayment bool) error {
func (s *loopOutSwap) handlePaymentResult(ctx context.Context,
result paymentResult, swapPayment bool) error {
switch {
// If our result has a non-nil error, our status will be nil. In this
@ -490,6 +518,17 @@ func (s *loopOutSwap) handlePaymentResult(result paymentResult,
// the swap payment and the prepay.
s.cost.Offchain += result.status.Fee.ToSatoshis()
// If this is an asset payment, we'll write the asset amounts
// to the swap.
if s.isAssetSwap() {
err := s.fillAssetOffchainPaymentResult(
ctx, result, swapPayment,
)
if err != nil {
return err
}
}
return nil
case result.status.State == lnrpc.Payment_FAILED:
@ -633,20 +672,31 @@ func (s *loopOutSwap) payInvoices(ctx context.Context) {
}
// Use the recommended routing plugin.
var assetSwapRfq []byte
if s.isAssetSwap() {
assetSwapRfq = s.AssetSwapInfo.SwapRfqId
}
s.swapPaymentChan = s.payInvoice(
ctx, s.SwapInvoice, s.MaxSwapRoutingFee,
s.LoopOutContract.OutgoingChanSet,
s.LoopOutContract.PaymentTimeout, pluginType, true,
assetSwapRfq,
)
// Pay the prepay invoice. Won't use the routing plugin here as the
// prepay is trivially small and shouldn't normally need any help. We
// are sending it over the same channel as the loop out payment.
s.log.Infof("Sending prepayment %v", s.PrepayInvoice)
var assetPrepayRfq []byte
if s.isAssetSwap() {
assetPrepayRfq = s.AssetSwapInfo.PrepayRfqId
}
s.prePaymentChan = s.payInvoice(
ctx, s.PrepayInvoice, s.MaxPrepayRoutingFee,
s.LoopOutContract.OutgoingChanSet,
s.LoopOutContract.PaymentTimeout, RoutingPluginNone, false,
assetPrepayRfq,
)
}
@ -675,7 +725,7 @@ func (p paymentResult) failure() error {
func (s *loopOutSwap) payInvoice(ctx context.Context, invoice string,
maxFee btcutil.Amount, outgoingChanIds loopdb.ChannelSet,
paymentTimeout time.Duration, pluginType RoutingPluginType,
reportPluginResult bool) chan paymentResult {
reportPluginResult bool, rfqId []byte) chan paymentResult {
resultChan := make(chan paymentResult)
sendResult := func(result paymentResult) {
@ -690,7 +740,7 @@ func (s *loopOutSwap) payInvoice(ctx context.Context, invoice string,
status, err := s.payInvoiceAsync(
ctx, invoice, maxFee, outgoingChanIds, paymentTimeout,
pluginType, reportPluginResult,
pluginType, reportPluginResult, rfqId,
)
if err != nil {
result.err = err
@ -719,7 +769,7 @@ func (s *loopOutSwap) payInvoice(ctx context.Context, invoice string,
func (s *loopOutSwap) payInvoiceAsync(ctx context.Context,
invoice string, maxFee btcutil.Amount,
outgoingChanIds loopdb.ChannelSet, paymentTimeout time.Duration,
pluginType RoutingPluginType, reportPluginResult bool) (
pluginType RoutingPluginType, reportPluginResult bool, rfqId []byte) (
*lndclient.PaymentStatus, error) {
// Extract hash from payment request. Unfortunately the request
@ -782,6 +832,25 @@ func (s *loopOutSwap) payInvoiceAsync(ctx context.Context,
MaxParts: s.executeConfig.loopOutMaxParts,
}
// If we want an asset swap, we'll need to set the custom first hop
// data to the rfq id. This will then allow LND to route the payment
// through the asset channel, as the edge nodes tap will know about the
// payment through the rfq id.
if s.isAssetSwap() {
var rfq rfqmsg.ID
if n := copy(rfq[:], rfqId); n != 32 {
return nil, fmt.Errorf("rfq id has wrong length: %v", n)
}
htlc := rfqmsg.NewHtlc(nil, fn.Some(rfq))
htlcMapRecords, err := tlv.RecordsToMap(htlc.Records())
if err != nil {
return nil, err
}
req.FirstHopCustomRecords = htlcMapRecords
}
// Lookup state of the swap payment.
payCtx, cancel := context.WithCancel(ctx)
defer cancel()
@ -982,7 +1051,7 @@ func (s *loopOutSwap) waitForConfirmedHtlc(globalCtx context.Context) (
case result := <-s.swapPaymentChan:
s.swapPaymentChan = nil
err := s.handlePaymentResult(result, true)
err := s.handlePaymentResult(ctx, result, true)
if err != nil {
return nil, err
}
@ -1004,7 +1073,7 @@ func (s *loopOutSwap) waitForConfirmedHtlc(globalCtx context.Context) (
case result := <-s.prePaymentChan:
s.prePaymentChan = nil
err := s.handlePaymentResult(result, false)
err := s.handlePaymentResult(ctx, result, false)
if err != nil {
return nil, err
}
@ -1404,3 +1473,36 @@ func (s *loopOutSwap) canSweep() bool {
return true
}
func (s *loopOutSwap) fillAssetOffchainPaymentResult(ctx context.Context,
result paymentResult, isSwapPayment bool) error {
if len(result.status.Htlcs) == 0 {
return fmt.Errorf("no htlcs in payment result")
}
// We only expect one htlc in the result.
htlc := result.status.Htlcs[0]
var assetData rfqmsg.JsonHtlc
err := json.Unmarshal(htlc.Route.CustomChannelData, &assetData)
if err != nil {
return err
}
assetSendAmt := assetData.Balances[0].Amount
if isSwapPayment {
s.AssetSwapInfo.SwapPaidAmt = assetSendAmt
log.Debugf("Asset off-chain payment success: %v", assetSendAmt)
} else {
s.AssetSwapInfo.PrepayPaidAmt = assetSendAmt
}
return s.store.UpdateLoopOutAssetInfo(ctx, s.hash, s.AssetSwapInfo)
}
// isAssetSwap returns true if the swap is an asset swap.
func (s *loopOutSwap) isAssetSwap() bool {
return s.AssetSwapInfo != nil
}

View file

@ -181,7 +181,7 @@ func testLateHtlcPublish(t *testing.T) {
height := int32(600)
cfg := newSwapConfig(&lnd.LndServices, store, server)
cfg := newSwapConfig(&lnd.LndServices, store, server, nil)
testRequest.Expiry = height + testLoopOutMinOnChainCltvDelta
@ -282,7 +282,7 @@ func testCustomSweepConfTarget(t *testing.T) {
ctx.Lnd.SetFeeEstimate(DefaultSweepConfTarget, 10000)
cfg := newSwapConfig(
&lnd.LndServices, loopdb.NewStoreMock(t), server,
&lnd.LndServices, loopdb.NewStoreMock(t), server, nil,
)
initResult, err := newLoopOutSwap(
@ -522,7 +522,7 @@ func testPreimagePush(t *testing.T) {
)
cfg := newSwapConfig(
&lnd.LndServices, loopdb.NewStoreMock(t), server,
&lnd.LndServices, loopdb.NewStoreMock(t), server, nil,
)
initResult, err := newLoopOutSwap(
@ -786,7 +786,7 @@ func testFailedOffChainCancelation(t *testing.T) {
testReq.Expiry = lnd.Height + 20
cfg := newSwapConfig(
&lnd.LndServices, loopdb.NewStoreMock(t), server,
&lnd.LndServices, loopdb.NewStoreMock(t), server, nil,
)
initResult, err := newLoopOutSwap(
@ -940,7 +940,7 @@ func TestLoopOutMuSig2Sweep(t *testing.T) {
)
cfg := newSwapConfig(
&lnd.LndServices, loopdb.NewStoreMock(t), server,
&lnd.LndServices, loopdb.NewStoreMock(t), server, nil,
)
initResult, err := newLoopOutSwap(

File diff suppressed because it is too large Load diff

View file

@ -336,6 +336,19 @@ message LoopOutRequest {
as the timeout for the payment.
*/
uint32 payment_timeout = 19;
/*
The optional asset information to use for the swap. If set, the swap will
be paid in the specified asset using the provided edge node. An Asset client
must be connected to the loop client to use this feature.
*/
AssetLoopOutRequest asset_info = 20;
/*
The optional RFQ information to use for the swap. If set, the swap will
use the provided RFQs to pay for the swap invoice.
*/
AssetRfqInfo asset_rfq_info = 21;
}
/*
@ -539,6 +552,9 @@ message SwapStatus {
// An optional label given to the swap on creation.
string label = 15;
// If the swap was an asset swap, the asset information will be returned.
AssetLoopOutInfo asset_info = 19;
}
enum SwapType {
@ -685,6 +701,9 @@ message ListSwapsFilter {
// If specified on creation, the last hop of the swap.
bytes loop_in_last_hop = 5;
// If specified, only returns asset swaps.
bool asset_swap_only = 6;
}
message ListSwapsResponse {
@ -793,6 +812,12 @@ message QuoteRequest {
the same time.
*/
repeated string deposit_outpoints = 8;
/*
The optional asset information to use for the swap. If set, the quote will
be returned in the specified asset.
*/
AssetLoopOutRequest asset_info = 9;
}
message InQuoteResponse {
@ -856,6 +881,12 @@ message OutQuoteResponse {
The confirmation target to be used for the sweep of the on-chain HTLC.
*/
int32 conf_target = 6;
/*
If the request was for an asset swap, the quote will return the rfq ids
that will be used to pay for the swap and prepay invoices.
*/
AssetRfqInfo asset_rfq_info = 7;
}
message ProbeRequest {
@ -2000,3 +2031,72 @@ message StaticAddressLoopInResponse {
*/
uint32 payment_timeout_seconds = 11;
}
message AssetLoopOutRequest {
/*
The asset id to use to pay for the swap invoice. If set an
asset client is needed to set to be able to pay the invoice.
*/
bytes asset_id = 1;
/*
The node identity public key of the peer to ask for a quote for sending out
the assets and converting them to satoshis. This must be specified if
an asset id is set.
*/
bytes asset_edge_node = 2;
/*
An optional maximum multiplier for the rfq rate. If not set, the default
will be 1.1. This means if we request a loop out quote for 1 BTC, the off
chain cost will be at most 1.1 BTC.
*/
double max_limit_multiplier = 3;
/*
An optional expiry unix timestamp for when the rfq quote should expire.
*/
int64 expiry = 4;
}
message AssetRfqInfo {
/*
The Prepay RFQ ID to use to pay for the prepay invoice.
*/
bytes prepay_rfq_id = 1;
/*
The actual asset amt to prepay for the swap invoice.
*/
uint64 prepay_asset_amt = 2;
/*
The Swap RFQ ID to use to pay for the swap invoice.
*/
bytes swap_rfq_id = 3;
/*
The actual asset amt to swap for the swap invoice.
*/
uint64 swap_asset_amt = 4;
/*
The name of the asset to swap.
*/
string asset_name = 5;
}
message AssetLoopOutInfo {
/*
The asset id that was used to pay for the swap invoice.
*/
string asset_id = 1;
/*
The human readable name of the asset.
*/
string asset_name = 2;
/*
The total asset offchain cost of the swap.
*/
uint64 asset_cost_offchain = 3;
}

View file

@ -265,6 +265,38 @@
"type": "string"
},
"collectionFormat": "multi"
},
{
"name": "asset_info.asset_id",
"description": "The asset id to use to pay for the swap invoice. If set an\nasset client is needed to set to be able to pay the invoice.",
"in": "query",
"required": false,
"type": "string",
"format": "byte"
},
{
"name": "asset_info.asset_edge_node",
"description": "The node identity public key of the peer to ask for a quote for sending out\nthe assets and converting them to satoshis. This must be specified if\nan asset id is set.",
"in": "query",
"required": false,
"type": "string",
"format": "byte"
},
{
"name": "asset_info.max_limit_multiplier",
"description": "An optional maximum multiplier for the rfq rate. If not set, the default\nwill be 1.1. This means if we request a loop out quote for 1 BTC, the off\nchain cost will be at most 1.1 BTC.",
"in": "query",
"required": false,
"type": "number",
"format": "double"
},
{
"name": "asset_info.expiry",
"description": "An optional expiry unix timestamp for when the rfq quote should expire.",
"in": "query",
"required": false,
"type": "string",
"format": "int64"
}
],
"tags": [
@ -426,6 +458,38 @@
"type": "string"
},
"collectionFormat": "multi"
},
{
"name": "asset_info.asset_id",
"description": "The asset id to use to pay for the swap invoice. If set an\nasset client is needed to set to be able to pay the invoice.",
"in": "query",
"required": false,
"type": "string",
"format": "byte"
},
{
"name": "asset_info.asset_edge_node",
"description": "The node identity public key of the peer to ask for a quote for sending out\nthe assets and converting them to satoshis. This must be specified if\nan asset id is set.",
"in": "query",
"required": false,
"type": "string",
"format": "byte"
},
{
"name": "asset_info.max_limit_multiplier",
"description": "An optional maximum multiplier for the rfq rate. If not set, the default\nwill be 1.1. This means if we request a loop out quote for 1 BTC, the off\nchain cost will be at most 1.1 BTC.",
"in": "query",
"required": false,
"type": "number",
"format": "double"
},
{
"name": "asset_info.expiry",
"description": "An optional expiry unix timestamp for when the rfq quote should expire.",
"in": "query",
"required": false,
"type": "string",
"format": "int64"
}
],
"tags": [
@ -554,6 +618,13 @@
"required": false,
"type": "string",
"format": "byte"
},
{
"name": "list_swap_filter.asset_swap_only",
"description": "If specified, only returns asset swaps.",
"in": "query",
"required": false,
"type": "boolean"
}
],
"tags": [
@ -609,6 +680,78 @@
"description": "- `unknown`: Unknown address type\n- `p2tr`: Pay to taproot pubkey (`TAPROOT_PUBKEY` = 1)",
"title": "`AddressType` has to be one of:"
},
"looprpcAssetLoopOutInfo": {
"type": "object",
"properties": {
"asset_id": {
"type": "string",
"description": "The asset id that was used to pay for the swap invoice."
},
"asset_name": {
"type": "string",
"description": "The human readable name of the asset."
},
"asset_cost_offchain": {
"type": "string",
"format": "uint64",
"description": "The total asset offchain cost of the swap."
}
}
},
"looprpcAssetLoopOutRequest": {
"type": "object",
"properties": {
"asset_id": {
"type": "string",
"format": "byte",
"description": "The asset id to use to pay for the swap invoice. If set an\nasset client is needed to set to be able to pay the invoice."
},
"asset_edge_node": {
"type": "string",
"format": "byte",
"description": "The node identity public key of the peer to ask for a quote for sending out\nthe assets and converting them to satoshis. This must be specified if\nan asset id is set."
},
"max_limit_multiplier": {
"type": "number",
"format": "double",
"description": "An optional maximum multiplier for the rfq rate. If not set, the default\nwill be 1.1. This means if we request a loop out quote for 1 BTC, the off\nchain cost will be at most 1.1 BTC."
},
"expiry": {
"type": "string",
"format": "int64",
"description": "An optional expiry unix timestamp for when the rfq quote should expire."
}
}
},
"looprpcAssetRfqInfo": {
"type": "object",
"properties": {
"prepay_rfq_id": {
"type": "string",
"format": "byte",
"description": "The Prepay RFQ ID to use to pay for the prepay invoice."
},
"prepay_asset_amt": {
"type": "string",
"format": "uint64",
"description": "The actual asset amt to prepay for the swap invoice."
},
"swap_rfq_id": {
"type": "string",
"format": "byte",
"description": "The Swap RFQ ID to use to pay for the swap invoice."
},
"swap_asset_amt": {
"type": "string",
"format": "uint64",
"description": "The actual asset amt to swap for the swap invoice."
},
"asset_name": {
"type": "string",
"description": "The name of the asset to swap."
}
}
},
"looprpcAutoReason": {
"type": "string",
"enum": [
@ -1208,6 +1351,10 @@
"type": "string",
"format": "byte",
"description": "If specified on creation, the last hop of the swap."
},
"asset_swap_only": {
"type": "boolean",
"description": "If specified, only returns asset swaps."
}
}
},
@ -1385,6 +1532,14 @@
"type": "integer",
"format": "int64",
"description": "The timeout in seconds to use for off-chain payments. Note that the swap\npayment is attempted multiple times where each attempt will set this value\nas the timeout for the payment."
},
"asset_info": {
"$ref": "#/definitions/looprpcAssetLoopOutRequest",
"description": "The optional asset information to use for the swap. If set, the swap will\nbe paid in the specified asset using the provided edge node. An Asset client\nmust be connected to the loop client to use this feature."
},
"asset_rfq_info": {
"$ref": "#/definitions/looprpcAssetRfqInfo",
"description": "The optional RFQ information to use for the swap. If set, the swap will\nuse the provided RFQs to pay for the swap invoice."
}
}
},
@ -1483,6 +1638,10 @@
"type": "integer",
"format": "int32",
"description": "The confirmation target to be used for the sweep of the on-chain HTLC."
},
"asset_rfq_info": {
"$ref": "#/definitions/looprpcAssetRfqInfo",
"description": "If the request was for an asset swap, the quote will return the rfq ids\nthat will be used to pay for the swap and prepay invoices."
}
}
},
@ -1841,6 +2000,10 @@
"label": {
"type": "string",
"description": "An optional label given to the swap on creation."
},
"asset_info": {
"$ref": "#/definitions/looprpcAssetLoopOutInfo",
"description": "If the swap was an asset swap, the asset information will be returned."
}
}
},

View file

@ -103,6 +103,7 @@ func (f *FSM) InitHtlcAction(ctx context.Context,
Memo: "static address loop-in",
Expiry: 3600 * 24 * 365,
RouteHints: f.loopIn.RouteHints,
Private: true,
},
)
if err != nil {

View file

@ -5,6 +5,7 @@ import (
"time"
"github.com/lightninglabs/lndclient"
"github.com/lightninglabs/loop/assets"
"github.com/lightninglabs/loop/loopdb"
"github.com/lightninglabs/loop/swap"
"github.com/lightninglabs/loop/utils"
@ -79,14 +80,16 @@ type swapConfig struct {
lnd *lndclient.LndServices
store loopdb.SwapStore
server swapServerClient
assets *assets.TapdClient
}
func newSwapConfig(lnd *lndclient.LndServices, store loopdb.SwapStore,
server swapServerClient) *swapConfig {
server swapServerClient, assets *assets.TapdClient) *swapConfig {
return &swapConfig{
lnd: lnd,
store: store,
server: server,
assets: assets,
}
}