Compare commits

..

No commits in common. 'master' and 'v0.27.1-beta' have entirely different histories.

@ -20,7 +20,7 @@ env:
# If you change this value, please change it in the following files as well:
# /Dockerfile
GO_VERSION: 1.21.10
GO_VERSION: 1.20.4
jobs:
########################

@ -1,4 +1,4 @@
FROM --platform=${BUILDPLATFORM} golang:1.22-alpine as builder
FROM --platform=${BUILDPLATFORM} golang:1.20.4-alpine as builder
# Copy in the local repository to build from.
COPY . /go/src/github.com/lightningnetwork/loop

@ -34,11 +34,7 @@ ifneq ($(workers),)
LINT_WORKERS = --concurrency=$(workers)
endif
DOCKER_TOOLS = docker run \
--rm \
-v $(shell bash -c "go env GOCACHE || (mkdir -p /tmp/go-cache; echo /tmp/go-cache)"):/tmp/build/.cache \
-v $(shell bash -c "go env GOMODCACHE || (mkdir -p /tmp/go-modcache; echo /tmp/go-modcache)"):/tmp/build/.modcache \
-v $$(pwd):/build loop-tools
DOCKER_TOOLS = docker run -v $$(pwd):/build loop-tools
GREEN := "\\033[0;32m"
NC := "\\033[0m"

@ -12,7 +12,7 @@ import (
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/schnorr"
"github.com/btcsuite/btcd/btcutil"
"github.com/lightninglabs/aperture/l402"
"github.com/lightninglabs/aperture/lsat"
"github.com/lightninglabs/lndclient"
"github.com/lightninglabs/loop/loopdb"
"github.com/lightninglabs/loop/swap"
@ -56,8 +56,8 @@ var (
// globalCallTimeout is the maximum time any call of the client to the
// server is allowed to take, including the time it may take to get
// and pay for an L402 token.
globalCallTimeout = serverRPCTimeout + l402.PaymentTimeout
// and pay for an LSAT token.
globalCallTimeout = serverRPCTimeout + lsat.PaymentTimeout
// probeTimeout is the maximum time until a probe is allowed to take.
probeTimeout = 3 * time.Minute
@ -111,13 +111,13 @@ type ClientConfig struct {
// Lnd is an instance of the lnd proxy.
Lnd *lndclient.LndServices
// MaxL402Cost is the maximum price we are willing to pay to the server
// MaxLsatCost is the maximum price we are willing to pay to the server
// for the token.
MaxL402Cost btcutil.Amount
MaxLsatCost btcutil.Amount
// MaxL402Fee is the maximum that we are willing to pay in routing fees
// MaxLsatFee is the maximum that we are willing to pay in routing fees
// to obtain the token.
MaxL402Fee btcutil.Amount
MaxLsatFee btcutil.Amount
// LoopOutMaxParts defines the maximum number of parts that may be used
// for a loop out swap. When greater than one, a multi-part payment may
@ -138,12 +138,12 @@ func NewClient(dbDir string, loopDB loopdb.SwapStore,
sweeperDb sweepbatcher.BatcherStore, cfg *ClientConfig) (
*Client, func(), error) {
l402Store, err := l402.NewFileStore(dbDir)
lsatStore, err := lsat.NewFileStore(dbDir)
if err != nil {
return nil, nil, err
}
swapServerClient, err := newSwapServerClient(cfg, l402Store)
swapServerClient, err := newSwapServerClient(cfg, lsatStore)
if err != nil {
return nil, nil, err
}
@ -153,7 +153,7 @@ func NewClient(dbDir string, loopDB loopdb.SwapStore,
Server: swapServerClient,
Store: loopDB,
Conn: swapServerClient.conn,
L402Store: l402Store,
LsatStore: lsatStore,
CreateExpiryTimer: func(d time.Duration) <-chan time.Time {
return time.NewTimer(d).C
},
@ -177,18 +177,10 @@ func NewClient(dbDir string, loopDB loopdb.SwapStore,
return nil
}
sweepStore, err := sweepbatcher.NewSweepFetcherFromSwapStore(
loopDB, cfg.Lnd.ChainParams,
)
if err != nil {
return nil, nil, fmt.Errorf("sweepbatcher."+
"NewSweepFetcherFromSwapStore failed: %w", err)
}
batcher := sweepbatcher.NewBatcher(
cfg.Lnd.WalletKit, cfg.Lnd.ChainNotifier, cfg.Lnd.Signer,
swapServerClient.MultiMuSig2SignSweep, verifySchnorrSig,
cfg.Lnd.ChainParams, sweeperDb, sweepStore,
cfg.Lnd.ChainParams, sweeperDb, loopDB,
)
executor := newExecutor(&executorConfig{
@ -683,8 +675,7 @@ func (s *Client) LoopInQuote(ctx context.Context,
// Because the Private flag is set, we'll generate our own
// set of hop hints and use that
request.RouteHints, err = SelectHopHints(
ctx, s.lndServices.Client, request.Amount,
DefaultMaxHopHints, includeNodes,
ctx, s.lndServices, request.Amount, DefaultMaxHopHints, includeNodes,
)
if err != nil {
return nil, err

@ -106,7 +106,7 @@ func TestLoopOutFailOffchain(t *testing.T) {
ctx.finish()
}
// TestLoopOutFailWrongAmount asserts that the client checks the server invoice
// TestLoopOutWrongAmount asserts that the client checks the server invoice
// amounts.
func TestLoopOutFailWrongAmount(t *testing.T) {
defer test.Guard(t)()

@ -25,12 +25,6 @@ var instantOutCommand = cli.Command{
Usage: "the comma-separated list of short " +
"channel IDs of the channels to loop out",
},
cli.StringFlag{
Name: "addr",
Usage: "the optional address that the looped out funds " +
"should be sent to, if let blank the funds " +
"will go to lnd's wallet",
},
},
Action: instantOut,
}
@ -89,10 +83,7 @@ func instantOut(ctx *cli.Context) error {
fmt.Printf("Available reservations: \n\n")
for _, res := range confirmedReservations {
idx++
fmt.Printf("Reservation %v: shortid %x, amt %v, expiry "+
"height %v \n", idx, res.ReservationId[:3], res.Amount,
res.Expiry)
fmt.Printf("Reservation %v: %v \n", idx, res.Amount)
totalAmt += int64(res.Amount)
}
@ -184,7 +175,6 @@ func instantOut(ctx *cli.Context) error {
&looprpc.InstantOutRequest{
ReservationIds: selectedReservations,
OutgoingChanSet: outgoingChanSet,
DestAddr: ctx.String("addr"),
},
)
@ -202,31 +192,3 @@ func instantOut(ctx *cli.Context) error {
return nil
}
var listInstantOutsCommand = cli.Command{
Name: "listinstantouts",
Usage: "list all instant out swaps",
Description: `
List all instant out swaps.
`,
Action: listInstantOuts,
}
func listInstantOuts(ctx *cli.Context) error {
// First set up the swap client itself.
client, cleanup, err := getClient(ctx)
if err != nil {
return err
}
defer cleanup()
resp, err := client.ListInstantOuts(
context.Background(), &looprpc.ListInstantOutsRequest{},
)
if err != nil {
return err
}
printRespJSON(resp)
return nil
}

@ -3,7 +3,6 @@ package main
import (
"context"
"fmt"
"math"
"strconv"
"strings"
"time"
@ -93,14 +92,6 @@ var loopOutCommand = cli.Command{
"Not setting this flag therefore might " +
"result in a lower swap fee",
},
cli.DurationFlag{
Name: "payment_timeout",
Usage: "the timeout for each individual off-chain " +
"payment attempt. If not set, the default " +
"timeout of 1 hour will be used. As the " +
"payment might be retried, the actual total " +
"time may be longer",
},
forceFlag,
labelFlag,
verboseFlag,
@ -244,25 +235,6 @@ func loopOut(ctx *cli.Context) error {
}
}
var paymentTimeout int64
if ctx.IsSet("payment_timeout") {
parsedTimeout := ctx.Duration("payment_timeout")
if parsedTimeout.Truncate(time.Second) != parsedTimeout {
return fmt.Errorf("payment timeout must be a " +
"whole number of seconds")
}
paymentTimeout = int64(parsedTimeout.Seconds())
if paymentTimeout <= 0 {
return fmt.Errorf("payment timeout must be a " +
"positive value")
}
if paymentTimeout > math.MaxUint32 {
return fmt.Errorf("payment timeout is too large")
}
}
resp, err := client.LoopOut(context.Background(), &looprpc.LoopOutRequest{
Amt: int64(amt),
Dest: destAddr,
@ -280,7 +252,6 @@ func loopOut(ctx *cli.Context) error {
SwapPublicationDeadline: uint64(swapDeadline.Unix()),
Label: label,
Initiator: defaultInitiator,
PaymentTimeout: uint32(paymentTimeout),
})
if err != nil {
return err

@ -26,8 +26,8 @@ type printableToken struct {
var listAuthCommand = cli.Command{
Name: "listauth",
Usage: "list all L402 tokens",
Description: "Shows a list of all L402 tokens that loopd has paid for",
Usage: "list all LSAT tokens",
Description: "Shows a list of all LSAT tokens that loopd has paid for",
Action: listAuth,
}
@ -38,7 +38,7 @@ func listAuth(ctx *cli.Context) error {
}
defer cleanup()
resp, err := client.GetL402Tokens(
resp, err := client.GetLsatTokens(
context.Background(), &looprpc.TokensRequest{},
)
if err != nil {

@ -148,7 +148,7 @@ func main() {
listSwapsCommand, swapInfoCommand, getLiquidityParamsCommand,
setLiquidityRuleCommand, suggestSwapCommand, setParamsCommand,
getInfoCommand, abandonSwapCommand, reservationsCommands,
instantOutCommand, listInstantOutsCommand,
instantOutCommand,
}
err := app.Run(os.Args)

@ -3,7 +3,7 @@ package loop
import (
"time"
"github.com/lightninglabs/aperture/l402"
"github.com/lightninglabs/aperture/lsat"
"github.com/lightninglabs/lndclient"
"github.com/lightninglabs/loop/loopdb"
"google.golang.org/grpc"
@ -15,7 +15,7 @@ type clientConfig struct {
Server swapServerClient
Conn *grpc.ClientConn
Store loopdb.SwapStore
L402Store l402.Store
LsatStore lsat.Store
CreateExpiryTimer func(expiry time.Duration) <-chan time.Time
LoopOutMaxParts uint32
}

@ -1,204 +0,0 @@
package loop
import (
"context"
"fmt"
"time"
"github.com/btcsuite/btcd/chaincfg"
"github.com/lightninglabs/lndclient"
"github.com/lightninglabs/loop/loopdb"
"github.com/lightninglabs/loop/swap"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwire"
)
const (
// costMigrationID is the identifier for the cost migration.
costMigrationID = "cost_migration"
// paymentBatchSize is the maximum number of payments we'll fetch in
// one go.
paymentBatchSize = 1000
)
// CalculateLoopOutCost calculates the total cost of a loop out swap. It will
// correctly account for the on-chain and off-chain fees that were paid and
// make sure that all costs are positive.
func CalculateLoopOutCost(params *chaincfg.Params, loopOutSwap *loopdb.LoopOut,
paymentFees map[lntypes.Hash]lnwire.MilliSatoshi) (loopdb.SwapCost,
error) {
// First make sure that this swap is actually finished.
if loopOutSwap.State().State.IsPending() {
return loopdb.SwapCost{}, fmt.Errorf("swap is not yet finished")
}
// We first need to decode the prepay invoice to get the prepay hash and
// the prepay amount.
_, _, hash, prepayAmount, err := swap.DecodeInvoice(
params, loopOutSwap.Contract.PrepayInvoice,
)
if err != nil {
return loopdb.SwapCost{}, fmt.Errorf("unable to decode the "+
"prepay invoice: %v", err)
}
// The swap hash is given and we don't need to get it from the
// swap invoice, however we'll decode it anyway to get the invoice amount
// that was paid in case we don't have the payment anymore.
_, _, swapHash, swapPaymentAmount, err := swap.DecodeInvoice(
params, loopOutSwap.Contract.SwapInvoice,
)
if err != nil {
return loopdb.SwapCost{}, fmt.Errorf("unable to decode the "+
"swap invoice: %v", err)
}
var (
cost loopdb.SwapCost
swapPaid, prepayPaid bool
)
// Now that we have the prepay and swap amount, we can calculate the
// total cost of the swap. Note that we only need to account for the
// server cost in case the swap was successful or if the sweep timed
// out. Otherwise the server didn't pull the off-chain htlc nor the
// prepay.
switch loopOutSwap.State().State {
case loopdb.StateSuccess:
cost.Server = swapPaymentAmount + prepayAmount -
loopOutSwap.Contract.AmountRequested
swapPaid = true
prepayPaid = true
case loopdb.StateFailSweepTimeout:
cost.Server = prepayAmount
prepayPaid = true
default:
cost.Server = 0
}
// Now attempt to look up the actual payments so we can calculate the
// total routing costs.
prepayPaymentFee, ok := paymentFees[hash]
if prepayPaid && ok {
cost.Offchain += prepayPaymentFee.ToSatoshis()
} else {
log.Debugf("Prepay payment %s is missing, won't account for "+
"routing fees", hash)
}
swapPaymentFee, ok := paymentFees[swapHash]
if swapPaid && ok {
cost.Offchain += swapPaymentFee.ToSatoshis()
} else {
log.Debugf("Swap payment %s is missing, won't account for "+
"routing fees", swapHash)
}
// For the on-chain cost, just make sure that the cost is positive.
cost.Onchain = loopOutSwap.State().Cost.Onchain
if cost.Onchain < 0 {
cost.Onchain *= -1
}
return cost, nil
}
// MigrateLoopOutCosts will calculate the correct cost for all loop out swaps
// and override the cost values of the last update in the database.
func MigrateLoopOutCosts(ctx context.Context, lnd lndclient.LndServices,
db loopdb.SwapStore) error {
migrationDone, err := db.HasMigration(ctx, costMigrationID)
if err != nil {
return err
}
if migrationDone {
log.Infof("Cost cleanup migration already done, skipping")
return nil
}
log.Infof("Starting cost cleanup migration")
startTs := time.Now()
defer func() {
log.Infof("Finished cost cleanup migration in %v",
time.Since(startTs))
}()
// First we'll fetch all loop out swaps from the database.
loopOutSwaps, err := db.FetchLoopOutSwaps(ctx)
if err != nil {
return err
}
// Gather payment fees to a map for easier lookup.
paymentFees := make(map[lntypes.Hash]lnwire.MilliSatoshi)
offset := uint64(0)
for {
payments, err := lnd.Client.ListPayments(
ctx, lndclient.ListPaymentsRequest{
Offset: offset,
MaxPayments: paymentBatchSize,
},
)
if err != nil {
return err
}
if len(payments.Payments) == 0 {
break
}
for _, payment := range payments.Payments {
paymentFees[payment.Hash] = payment.Fee
}
offset = payments.LastIndexOffset + 1
}
// Now we'll calculate the cost for each swap and finally update the
// costs in the database.
updatedCosts := make(map[lntypes.Hash]loopdb.SwapCost)
for _, loopOutSwap := range loopOutSwaps {
if loopOutSwap.State().State.IsPending() {
continue
}
cost, err := CalculateLoopOutCost(
lnd.ChainParams, loopOutSwap, paymentFees,
)
if err != nil {
// We don't want to fail loopd because of any old swap
// that we're unable to calculate the cost for. We'll
// warn though so that we can investigate further.
log.Warnf("Unable to calculate cost for swap %v: %v",
loopOutSwap.Hash, err)
continue
}
_, ok := updatedCosts[loopOutSwap.Hash]
if ok {
return fmt.Errorf("found a duplicate swap %v while "+
"updating costs", loopOutSwap.Hash)
}
updatedCosts[loopOutSwap.Hash] = cost
}
log.Infof("Updating costs for %d loop out swaps", len(updatedCosts))
err = db.BatchUpdateLoopOutSwapCosts(ctx, updatedCosts)
if err != nil {
return err
}
// Finally mark the migration as done.
return db.SetMigration(ctx, costMigrationID)
}

@ -1,184 +0,0 @@
package loop
import (
"context"
"testing"
"time"
"github.com/btcsuite/btcd/btcutil"
"github.com/lightninglabs/lndclient"
"github.com/lightninglabs/loop/loopdb"
"github.com/lightninglabs/loop/test"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/stretchr/testify/require"
)
// TestCalculateLoopOutCost tests the CalculateLoopOutCost function.
func TestCalculateLoopOutCost(t *testing.T) {
// Set up test context objects.
lnd := test.NewMockLnd()
server := newServerMock(lnd)
store := loopdb.NewStoreMock(t)
cfg := &swapConfig{
lnd: &lnd.LndServices,
store: store,
server: server,
}
height := int32(600)
req := *testRequest
initResult, err := newLoopOutSwap(
context.Background(), cfg, height, &req,
)
require.NoError(t, err)
swap, err := store.FetchLoopOutSwap(
context.Background(), initResult.swap.hash,
)
require.NoError(t, err)
// Override the chain cost so it's negative.
const expectedChainCost = btcutil.Amount(1000)
// Now we have the swap and prepay invoices so let's calculate the
// costs without providing the payments first, so we don't account for
// any routing fees.
paymentFees := make(map[lntypes.Hash]lnwire.MilliSatoshi)
_, err = CalculateLoopOutCost(lnd.ChainParams, swap, paymentFees)
// We expect that the call fails as the swap isn't finished yet.
require.Error(t, err)
// Override the swap state to make it look like the swap is finished
// and make the chain cost negative too, so we can test that it'll be
// corrected to be positive in the cost calculation.
swap.Events = append(
swap.Events, &loopdb.LoopEvent{
SwapStateData: loopdb.SwapStateData{
State: loopdb.StateSuccess,
Cost: loopdb.SwapCost{
Onchain: -expectedChainCost,
},
},
},
)
costs, err := CalculateLoopOutCost(lnd.ChainParams, swap, paymentFees)
require.NoError(t, err)
expectedServerCost := server.swapInvoiceAmt + server.prepayInvoiceAmt -
swap.Contract.AmountRequested
require.Equal(t, expectedServerCost, costs.Server)
require.Equal(t, btcutil.Amount(0), costs.Offchain)
require.Equal(t, expectedChainCost, costs.Onchain)
// Now add the two payments to the payments map and calculate the costs
// again. We expect that the routng fees are now accounted for.
paymentFees[server.swapHash] = lnwire.NewMSatFromSatoshis(44)
paymentFees[server.prepayHash] = lnwire.NewMSatFromSatoshis(11)
costs, err = CalculateLoopOutCost(lnd.ChainParams, swap, paymentFees)
require.NoError(t, err)
expectedOffchainCost := btcutil.Amount(44 + 11)
require.Equal(t, expectedServerCost, costs.Server)
require.Equal(t, expectedOffchainCost, costs.Offchain)
require.Equal(t, expectedChainCost, costs.Onchain)
// Now override the last update to make the swap timed out at the HTLC
// sweep. We expect that the chain cost won't change, and only the
// prepay will be accounted for.
swap.Events[0] = &loopdb.LoopEvent{
SwapStateData: loopdb.SwapStateData{
State: loopdb.StateFailSweepTimeout,
Cost: loopdb.SwapCost{
Onchain: 0,
},
},
}
costs, err = CalculateLoopOutCost(lnd.ChainParams, swap, paymentFees)
require.NoError(t, err)
expectedServerCost = server.prepayInvoiceAmt
expectedOffchainCost = btcutil.Amount(11)
require.Equal(t, expectedServerCost, costs.Server)
require.Equal(t, expectedOffchainCost, costs.Offchain)
require.Equal(t, btcutil.Amount(0), costs.Onchain)
}
// TestCostMigration tests the cost migration for loop out swaps.
func TestCostMigration(t *testing.T) {
// Set up test context objects.
lnd := test.NewMockLnd()
server := newServerMock(lnd)
store := loopdb.NewStoreMock(t)
cfg := &swapConfig{
lnd: &lnd.LndServices,
store: store,
server: server,
}
height := int32(600)
req := *testRequest
initResult, err := newLoopOutSwap(
context.Background(), cfg, height, &req,
)
require.NoError(t, err)
// Override the chain cost so it's negative.
const expectedChainCost = btcutil.Amount(1000)
// Override the swap state to make it look like the swap is finished
// and make the chain cost negative too, so we can test that it'll be
// corrected to be positive in the cost calculation.
err = store.UpdateLoopOut(
context.Background(), initResult.swap.hash, time.Now(),
loopdb.SwapStateData{
State: loopdb.StateSuccess,
Cost: loopdb.SwapCost{
Onchain: -expectedChainCost,
},
},
)
require.NoError(t, err)
// Add the two mocked payment to LND. Note that we only care about the
// fees here, so we don't need to provide the full payment details.
lnd.Payments = []lndclient.Payment{
{
Hash: server.swapHash,
Fee: lnwire.NewMSatFromSatoshis(44),
},
{
Hash: server.prepayHash,
Fee: lnwire.NewMSatFromSatoshis(11),
},
}
// Now we can run the migration.
err = MigrateLoopOutCosts(context.Background(), lnd.LndServices, store)
require.NoError(t, err)
// Finally check that the swap cost has been updated correctly.
swap, err := store.FetchLoopOutSwap(
context.Background(), initResult.swap.hash,
)
require.NoError(t, err)
expectedServerCost := server.swapInvoiceAmt + server.prepayInvoiceAmt -
swap.Contract.AmountRequested
costs := swap.Events[0].Cost
expectedOffchainCost := btcutil.Amount(44 + 11)
require.Equal(t, expectedServerCost, costs.Server)
require.Equal(t, expectedOffchainCost, costs.Offchain)
require.Equal(t, expectedChainCost, costs.Onchain)
// Now run the migration again to make sure it doesn't fail. This also
// indicates that the migration did not run the second time as
// otherwise the store mocks SetMigration function would fail.
err = MigrateLoopOutCosts(context.Background(), lnd.LndServices, store)
require.NoError(t, err)
}

@ -243,82 +243,3 @@ func TestExampleFSMFlow(t *testing.T) {
})
}
}
// TestObserverAsyncWait tests the observer's WaitForStateAsync function.
func TestObserverAsyncWait(t *testing.T) {
testCases := []struct {
name string
waitTime time.Duration
blockTime time.Duration
expectTimeout bool
}{
{
name: "success",
waitTime: time.Second,
blockTime: time.Millisecond,
expectTimeout: false,
},
{
name: "timeout",
waitTime: time.Millisecond,
blockTime: time.Second,
expectTimeout: true,
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
service := &mockService{
respondChan: make(chan bool),
}
store := &mockStore{}
exampleContext := NewExampleFSMContext(service, store)
cachedObserver := NewCachedObserver(100)
exampleContext.RegisterObserver(cachedObserver)
t0 := time.Now()
timeoutCtx, cancel := context.WithTimeout(
context.Background(), tc.waitTime,
)
defer cancel()
// Wait for the final state.
errChan := cachedObserver.WaitForStateAsync(
timeoutCtx, StuffSuccess, true,
)
go func() {
err := exampleContext.SendEvent(
OnRequestStuff,
newInitStuffRequest(),
)
require.NoError(t, err)
time.Sleep(tc.blockTime)
service.respondChan <- true
}()
timeout := false
select {
case <-timeoutCtx.Done():
timeout = true
case <-errChan:
}
require.Equal(t, tc.expectTimeout, timeout)
t1 := time.Now()
diff := t1.Sub(t0)
if tc.expectTimeout {
require.Less(t, diff, tc.blockTime)
} else {
require.Less(t, diff, tc.waitTime)
}
})
}
}

@ -306,36 +306,23 @@ func NoOpAction(_ EventContext) EventType {
}
// ErrConfigError is an error returned when the state machine is misconfigured.
type ErrConfigError struct {
msg string
}
// Error returns the error message.
func (e ErrConfigError) Error() string {
return fmt.Sprintf("config error: %s", e.msg)
}
type ErrConfigError error
// NewErrConfigError creates a new ErrConfigError.
func NewErrConfigError(msg string) ErrConfigError {
return ErrConfigError{
msg: msg,
}
return (ErrConfigError)(fmt.Errorf("config error: %s", msg))
}
// ErrWaitingForStateTimeout is an error returned when the state machine times
// out while waiting for a state.
type ErrWaitingForStateTimeout struct {
expected StateType
}
// Error returns the error message.
func (e ErrWaitingForStateTimeout) Error() string {
return fmt.Sprintf("waiting for state timed out: %s", e.expected)
}
type ErrWaitingForStateTimeout error
// NewErrWaitingForStateTimeout creates a new ErrWaitingForStateTimeout.
func NewErrWaitingForStateTimeout(expected StateType) ErrWaitingForStateTimeout {
return ErrWaitingForStateTimeout{
expected: expected,
}
func NewErrWaitingForStateTimeout(expected,
actual StateType) ErrWaitingForStateTimeout {
return (ErrWaitingForStateTimeout)(fmt.Errorf(
"waiting for state timeout: expected %s, actual: %s",
expected, actual,
))
}

@ -100,11 +100,12 @@ func WithAbortEarlyOnErrorOption() WaitForStateOption {
// the given duration before checking the state. This is useful if the
// function is called immediately after sending an event to the state machine
// and the state machine needs some time to process the event.
func (c *CachedObserver) WaitForState(ctx context.Context,
func (s *CachedObserver) WaitForState(ctx context.Context,
timeout time.Duration, state StateType,
opts ...WaitForStateOption) error {
var options fsmOptions
for _, opt := range opts {
opt.apply(&options)
}
@ -119,77 +120,61 @@ func (c *CachedObserver) WaitForState(ctx context.Context,
}
}
// Create a new context with a timeout.
timeoutCtx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()
ch := c.WaitForStateAsync(timeoutCtx, state, options.abortEarlyOnError)
// Wait for either the condition to be met or for a timeout.
select {
case <-timeoutCtx.Done():
return NewErrWaitingForStateTimeout(state)
// Channel to notify when the desired state is reached
// or an error occurred.
ch := make(chan error)
case err := <-ch:
return err
}
}
// WaitForStateAsync waits asynchronously until the passed context is canceled
// or the expected state is reached. The function returns a channel that will
// receive an error if the expected state is reached or an error occurred. If
// the context is canceled before the expected state is reached, the channel
// will receive an ErrWaitingForStateTimeout error.
func (c *CachedObserver) WaitForStateAsync(ctx context.Context, state StateType,
abortOnEarlyError bool) chan error {
// Channel to notify when the desired state is reached or an error
// occurred.
ch := make(chan error, 1)
// Wait on the notification condition variable asynchronously to avoid
// blocking the caller.
// Goroutine to wait on condition variable
go func() {
c.notificationMx.Lock()
defer c.notificationMx.Unlock()
// writeResult writes the result to the channel. If the context
// is canceled, an ErrWaitingForStateTimeout error is written
// to the channel.
writeResult := func(err error) {
select {
case <-ctx.Done():
ch <- NewErrWaitingForStateTimeout(
state,
)
case ch <- err:
}
}
s.notificationMx.Lock()
defer s.notificationMx.Unlock()
for {
// Check if the last state is the desired state.
if c.lastNotification.NextState == state {
writeResult(nil)
return
}
// Check if the last state is the desired state
if s.lastNotification.NextState == state {
select {
case <-timeoutCtx.Done():
return
// Check if an error has occurred.
if c.lastNotification.Event == OnError {
lastErr := c.lastNotification.LastActionError
if abortOnEarlyError {
writeResult(lastErr)
case ch <- nil:
return
}
}
// Otherwise use the conditional variable to wait for
// the next notification.
c.notificationCond.Wait()
// Check if an error occurred
if s.lastNotification.Event == OnError {
if options.abortEarlyOnError {
select {
case <-timeoutCtx.Done():
return
case ch <- s.lastNotification.LastActionError:
return
}
}
}
// Otherwise, wait for the next notification
s.notificationCond.Wait()
}
}()
return ch
// Wait for either the condition to be met or for a timeout
select {
case <-timeoutCtx.Done():
return NewErrWaitingForStateTimeout(
state, s.lastNotification.NextState,
)
case lastActionErr := <-ch:
if lastActionErr != nil {
return lastActionErr
}
return nil
}
}
// FixedSizeSlice is a slice with a fixed size.

136
go.mod

@ -1,42 +1,42 @@
module github.com/lightninglabs/loop
require (
github.com/btcsuite/btcd v0.24.2-beta.rc1.0.20240403021926-ae5533602c46
github.com/btcsuite/btcd/btcec/v2 v2.3.3
github.com/btcsuite/btcd v0.24.1-0.20240123000108-62e6af035ec5
github.com/btcsuite/btcd/btcec/v2 v2.3.2
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.20240404104514-b2f31f9045fb
github.com/btcsuite/btcwallet/wtxmgr v1.5.3
github.com/btcsuite/btcwallet v0.16.10-0.20240127010340-16b422a2e8bf
github.com/btcsuite/btcwallet/wtxmgr v1.5.0
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/decred/dcrd/dcrec/secp256k1/v4 v4.0.1
github.com/fortytw2/leaktest v1.3.0
github.com/golang-migrate/migrate/v4 v4.17.0
github.com/golang-migrate/migrate/v4 v4.16.1
github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3
github.com/jackc/pgconn v1.14.3
github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438
github.com/jackc/pgconn v1.14.0
github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa
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.0-1
github.com/lib/pq v1.10.7
github.com/lightninglabs/aperture v0.1.21-beta.0.20230705004936-87bb996a4030
github.com/lightninglabs/lndclient v0.17.4-1
github.com/lightninglabs/loop/swapserverrpc v1.0.5
github.com/lightningnetwork/lnd v0.18.0-beta.1
github.com/lightningnetwork/lnd v0.17.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/tor v1.1.2
github.com/ory/dockertest/v3 v3.10.0
github.com/stretchr/testify v1.9.0
github.com/stretchr/testify v1.8.4
github.com/urfave/cli v1.22.9
golang.org/x/net v0.23.0
golang.org/x/net v0.17.0
google.golang.org/grpc v1.59.0
google.golang.org/protobuf v1.33.0
google.golang.org/protobuf v1.31.0
gopkg.in/macaroon-bakery.v2 v2.1.0
gopkg.in/macaroon.v2 v2.1.0
modernc.org/sqlite v1.29.8
modernc.org/sqlite v1.20.3
)
require (
@ -48,11 +48,12 @@ require (
github.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344 // indirect
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect
github.com/aead/siphash v1.0.1 // indirect
github.com/andybalholm/brotli v1.0.4 // 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.2 // indirect
github.com/btcsuite/btcwallet/wallet/txrules v1.2.0 // indirect
github.com/btcsuite/btcwallet/wallet/txsizes v1.2.3 // indirect
github.com/btcsuite/btcwallet/walletdb v1.4.0 // 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
@ -63,84 +64,88 @@ require (
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f // 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/decred/dcrd/crypto/blake256 v1.0.0 // indirect
github.com/decred/dcrd/lru v1.0.0 // indirect
github.com/docker/cli v20.10.17+incompatible // indirect
github.com/docker/docker v24.0.9+incompatible // indirect
github.com/docker/docker v24.0.7+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/fergusstrange/embedded-postgres v1.25.0 // indirect
github.com/dsnet/compress v0.0.1 // indirect
github.com/dustin/go-humanize v1.0.0 // indirect
github.com/fergusstrange/embedded-postgres v1.10.0 // indirect
github.com/go-errors/errors v1.0.1 // indirect
github.com/go-logr/logr v1.3.0 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v4 v4.4.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/btree v1.0.1 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/google/uuid v1.3.1 // 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-prometheus v1.2.0 // indirect
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/imdario/mergo v0.3.13 // indirect
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
github.com/jackc/pgio v1.0.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgproto3/v2 v2.3.3 // indirect
github.com/jackc/pgproto3/v2 v2.3.2 // indirect
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
github.com/jackc/pgtype v1.14.0 // indirect
github.com/jackc/pgx/v4 v4.18.2 // indirect
github.com/jackc/pgx/v4 v4.18.1 // indirect
github.com/jackpal/gateway v1.0.5 // indirect
github.com/jackpal/go-nat-pmp v0.0.0-20170405195558-28a68d0c24ad // indirect
github.com/jonboulle/clockwork v0.2.2 // indirect
github.com/jrick/logrotate v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/juju/loggo v0.0.0-20210728185423-eebad3a902c4 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/kkdai/bstream v1.0.0 // indirect
github.com/klauspost/compress v1.15.9 // indirect
github.com/klauspost/pgzip v1.2.5 // indirect
github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf // indirect
github.com/lightninglabs/neutrino v0.16.1-0.20240425105051-602843d34ffd // indirect
github.com/lightninglabs/neutrino/cache v1.1.2 // indirect
github.com/lightninglabs/neutrino v0.16.0 // indirect
github.com/lightninglabs/neutrino/cache v1.1.1 // indirect
github.com/lightningnetwork/lightning-onion v1.2.1-0.20230823005744-06182b1d7d2f // indirect
github.com/lightningnetwork/lnd/fn v1.0.5 // indirect
github.com/lightningnetwork/lnd/healthcheck v1.2.4 // indirect
github.com/lightningnetwork/lnd/kvdb v1.4.8 // indirect
github.com/lightningnetwork/lnd/sqldb v1.0.2 // indirect
github.com/lightningnetwork/lnd/tlv v1.2.3 // indirect
github.com/lightningnetwork/lnd/healthcheck v1.2.3 // indirect
github.com/lightningnetwork/lnd/kvdb v1.4.4 // indirect
github.com/lightningnetwork/lnd/tlv v1.1.1 // indirect
github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
github.com/mholt/archiver/v3 v3.5.0 // indirect
github.com/miekg/dns v1.1.43 // indirect
github.com/mitchellh/mapstructure v1.4.1 // indirect
github.com/moby/term v0.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/nwaples/rardecode v1.1.2 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.2 // indirect
github.com/opencontainers/runc v1.1.12 // indirect
github.com/pierrec/lz4/v4 v4.1.15 // 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_model v0.3.0 // indirect
github.com/prometheus/common v0.30.0 // indirect
github.com/prometheus/procfs v0.7.3 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
github.com/rogpeppe/fastuuid v1.2.0 // indirect
github.com/russross/blackfriday/v2 v2.0.1 // indirect
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
github.com/sirupsen/logrus v1.9.2 // indirect
github.com/soheilhy/cmux v0.1.5 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/stretchr/objx v0.5.0 // indirect
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect
github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 // indirect
github.com/tv42/zbase32 v0.0.0-20160707012821-501572607d02 // indirect
github.com/ulikunitz/xz v0.5.11 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
@ -155,47 +160,50 @@ require (
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.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 // indirect
go.opentelemetry.io/otel v1.21.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.0 // indirect
go.opentelemetry.io/otel v1.20.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.21.0 // indirect
go.opentelemetry.io/otel/sdk v1.21.0 // indirect
go.opentelemetry.io/otel/trace v1.21.0 // indirect
go.opentelemetry.io/otel/metric v1.20.0 // indirect
go.opentelemetry.io/otel/sdk v1.3.0 // indirect
go.opentelemetry.io/otel/trace v1.20.0 // indirect
go.opentelemetry.io/proto/otlp v0.19.0 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.17.0 // indirect
golang.org/x/crypto v0.22.0 // indirect
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 // indirect
golang.org/x/mod v0.16.0 // indirect
golang.org/x/sync v0.6.0 // indirect
golang.org/x/sys v0.19.0 // indirect
golang.org/x/term v0.19.0 // indirect
golang.org/x/crypto v0.17.0 // indirect
golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0 // indirect
golang.org/x/mod v0.10.0 // indirect
golang.org/x/sync v0.3.0 // indirect
golang.org/x/sys v0.15.0 // indirect
golang.org/x/term v0.15.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.19.0 // indirect
google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 // indirect
golang.org/x/tools v0.9.1 // indirect
google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect
gopkg.in/errgo.v1 v1.0.1 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
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/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
lukechampine.com/uint128 v1.2.0 // indirect
modernc.org/cc/v3 v3.40.0 // indirect
modernc.org/ccgo/v3 v3.16.13 // indirect
modernc.org/libc v1.22.2 // indirect
modernc.org/mathutil v1.5.0 // indirect
modernc.org/memory v1.4.0 // indirect
modernc.org/opt v0.1.3 // indirect
modernc.org/strutil v1.1.3 // indirect
modernc.org/token v1.0.1 // indirect
sigs.k8s.io/yaml v1.2.0 // indirect
)
// We want to format raw bytes as hex instead of base64. The forked version
// allows us to specify that as an option.
replace google.golang.org/protobuf => github.com/lightninglabs/protobuf-go-hex-display v1.33.0-hex-display
replace google.golang.org/protobuf => github.com/lightninglabs/protobuf-go-hex-display v1.30.0-hex-display
replace github.com/lightninglabs/loop/swapserverrpc => ./swapserverrpc
go 1.22.3
go 1.19

334
go.sum

@ -36,7 +36,7 @@ cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRY
cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM=
cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I=
cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY=
cloud.google.com/go v0.110.10 h1:LXy9GEO+timppncPIAZoOj3l58LIU9k+kn48AN7IO3Y=
cloud.google.com/go v0.110.7 h1:rJyC7nWRg2jWGZ4wSJ5nY65GTdYJkg0cd/uXb+ACI6o=
cloud.google.com/go/accessapproval v1.4.0/go.mod h1:zybIuC3KpDOvotz59lFe5qxRZx6C75OtwbisN56xYB4=
cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw=
cloud.google.com/go/accessapproval v1.6.0/go.mod h1:R0EiYnwV5fsRFiKZkPHr6mwyk2wxUJ30nL4j2pcFY2E=
@ -173,8 +173,7 @@ cloud.google.com/go/compute v1.15.1/go.mod h1:bjjoF/NtFUrkD/urWfdHaKuOPDR5nWIs63
cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs=
cloud.google.com/go/compute v1.19.0/go.mod h1:rikpw2y+UMidAe9tISo04EHNOIf42RLYF/q8Bs93scU=
cloud.google.com/go/compute v1.19.1/go.mod h1:6ylj3a05WF8leseCdIf77NK0g1ey+nj5IKd5/kvShxE=
cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk=
cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI=
cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY=
cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU=
cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM=
@ -607,7 +606,6 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym
github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk=
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g=
github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/NebulousLabs/fastrand v0.0.0-20181203155948-6fb6489aac4e h1:n+DcnTNkQnHlwpsrHoQtkrJIO7CBx029fw6oR4vIob4=
@ -632,6 +630,8 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/apache/arrow/go/v10 v10.0.1/go.mod h1:YvhnlEePVnBS4+0z3fhPfUy7W1Ikj0Ih0vcRo/gZ1M0=
@ -645,15 +645,19 @@ github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl
github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M=
github.com/btcsuite/btcd v0.22.0-beta.0.20220204213055-eaf0459ff879/go.mod h1:osu7EoKiL36UThEgzYPqdRaxeo0NU8VoXqgcnwpey0g=
github.com/btcsuite/btcd v0.23.1/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY=
github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd/go.mod h1:nm3Bko6zh6bWP60UxwoT5LzdGJsQJaPo6HjduXq9p6A=
github.com/btcsuite/btcd v0.24.2-beta.rc1.0.20240403021926-ae5533602c46 h1:tjpNTdZNQqE14menwDGAxWfzN0DFHVTXFEyEL8yvA/4=
github.com/btcsuite/btcd v0.24.2-beta.rc1.0.20240403021926-ae5533602c46/go.mod h1:5C8ChTkl5ejr3WHj8tkQSCmydiMEPB0ZhQhehpq7Dgg=
github.com/btcsuite/btcd v0.24.1-0.20240123000108-62e6af035ec5 h1:8BHBWvtP6kkzvmCpyWEznq4eS0gfLOSVuXLesv413Xs=
github.com/btcsuite/btcd v0.24.1-0.20240123000108-62e6af035ec5/go.mod h1:5C8ChTkl5ejr3WHj8tkQSCmydiMEPB0ZhQhehpq7Dgg=
github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA=
github.com/btcsuite/btcd/btcec/v2 v2.1.1/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE=
github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE=
github.com/btcsuite/btcd/btcec/v2 v2.3.3 h1:6+iXlDKE8RMtKsvK0gshlXIuPbyWM/h84Ensb7o3sC0=
github.com/btcsuite/btcd/btcec/v2 v2.3.3/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04=
github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U=
github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04=
github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A=
github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE=
github.com/btcsuite/btcd/btcutil v1.1.1/go.mod h1:nbKlBMNm9FGsdvKvu0essceubPiAcI57pYBNnsLAa34=
github.com/btcsuite/btcd/btcutil v1.1.5 h1:+wER79R5670vs/ZusMTF1yTcRYE5GUsFbdjdisflzM8=
github.com/btcsuite/btcd/btcutil v1.1.5/go.mod h1:PSZZ4UitpLBWzxGd5VGOrLnmOjtPP/a6HaFo12zMs00=
github.com/btcsuite/btcd/btcutil/psbt v1.1.8 h1:4voqtT8UppT7nmKQkXV+T9K8UyQjKOn2z/ycpmJK8wg=
@ -665,18 +669,20 @@ github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtyd
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/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
github.com/btcsuite/btcwallet v0.16.10-0.20240404104514-b2f31f9045fb h1:qoIOlBPRZWtfpcbQlNFf67Wz8ZlXo+mxQc9Pnbm/iqU=
github.com/btcsuite/btcwallet v0.16.10-0.20240404104514-b2f31f9045fb/go.mod h1:2C3Q/MhYAKmk7F+Tey6LfKtKRTdQsrCf8AAAzzDPmH4=
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.20240127010340-16b422a2e8bf h1:eNjj5R0tKP48NQxDkuKr+C9frZsdzTAemEwu75ZDQg0=
github.com/btcsuite/btcwallet v0.16.10-0.20240127010340-16b422a2e8bf/go.mod h1:LzcW/LYkQLgDufv6Ouw4cOIW0YsY+A60MTtc61/OZTU=
github.com/btcsuite/btcwallet/wallet/txauthor v1.3.2 h1:etuLgGEojecsDOYTII8rYiGHjGyV5xTqsXi+ZQ715UU=
github.com/btcsuite/btcwallet/wallet/txauthor v1.3.2/go.mod h1:Zpk/LOb2sKqwP2lmHjaZT9AdaKsHPSbNLm2Uql5IQ/0=
github.com/btcsuite/btcwallet/wallet/txrules v1.2.0 h1:BtEN5Empw62/RVnZ0VcJaVtVlBijnLlJY+dwjAye2Bg=
github.com/btcsuite/btcwallet/wallet/txrules v1.2.0/go.mod h1:AtkqiL7ccKWxuLYtZm8Bu8G6q82w4yIZdgq6riy60z0=
github.com/btcsuite/btcwallet/wallet/txsizes v1.2.2/go.mod h1:q08Rms52VyWyXcp5zDc4tdFRKkFgNsMQrv3/LvE1448=
github.com/btcsuite/btcwallet/wallet/txsizes v1.2.3 h1:PszOub7iXVYbtGybym5TGCp9Dv1h1iX4rIC3HICZGLg=
github.com/btcsuite/btcwallet/wallet/txsizes v1.2.3/go.mod h1:q08Rms52VyWyXcp5zDc4tdFRKkFgNsMQrv3/LvE1448=
github.com/btcsuite/btcwallet/walletdb v1.3.5/go.mod h1:oJDxAEUHVtnmIIBaa22wSBPTVcs6hUp5NKWmI8xDwwU=
github.com/btcsuite/btcwallet/walletdb v1.4.0 h1:/C5JRF+dTuE2CNMCO/or5N8epsrhmSM4710uBQoYPTQ=
github.com/btcsuite/btcwallet/walletdb v1.4.0/go.mod h1:oJDxAEUHVtnmIIBaa22wSBPTVcs6hUp5NKWmI8xDwwU=
github.com/btcsuite/btcwallet/wtxmgr v1.5.0 h1:WO0KyN4l6H3JWnlFxfGR7r3gDnlGT7W2cL8vl6av4SU=
github.com/btcsuite/btcwallet/wtxmgr v1.5.0/go.mod h1:TQVDhFxseiGtZwEPvLgtfyxuNUDsIdaJdshvWzR0HJ4=
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=
@ -695,7 +701,6 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA
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.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
@ -716,17 +721,13 @@ github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWH
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+gqO04wryn5h75LSazbRlnya1k=
github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20231109132714-523115ebc101 h1:7To3pQ+pZo0i3dsWEbinPNFs5gPSBOsJtx3wTT94VBY=
github.com/cncf/xds/go v0.0.0-20231109132714-523115ebc101/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
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/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=
@ -744,36 +745,32 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsr
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0=
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y=
github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
github.com/decred/dcrd/lru v1.0.0 h1:Kbsb1SFDsIlaupWPwsPp+dkxiBY1frcS07PCPgotKz8=
github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218=
github.com/decred/dcrd/lru v1.1.2 h1:KdCzlkxppuoIDGEvCGah1fZRicrDH36IipvlB1ROkFY=
github.com/decred/dcrd/lru v1.1.2/go.mod h1:gEdCVgXs1/YoBvFWt7Scgknbhwik3FgVSzlnCcXL2N8=
github.com/dhui/dktest v0.4.0 h1:z05UmuXZHO/bgj/ds2bGMBu8FI4WA+Ag/m3ghL+om7M=
github.com/dhui/dktest v0.4.0/go.mod h1:v/Dbz1LgCBOi2Uki2nUqLBGa83hWBGFMu5MrgMDCc78=
github.com/dhui/dktest v0.3.16 h1:i6gq2YQEtcrjKbeJpBkWjE8MmLZPYllcjOFbTZuPDnw=
github.com/docker/cli v20.10.17+incompatible h1:eO2KS7ZFeov5UJeaDmIs1NFEDRf32PaqRpvoEkKBy5M=
github.com/docker/cli v20.10.17+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=
github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v24.0.9+incompatible h1:HPGzNmwfLZWdxHqK9/II92pyi1EpYKsAqcl4G0Of9v0=
github.com/docker/docker v24.0.9+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig1seQen0cKYlM=
github.com/docker/docker v24.0.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q=
github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo=
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
@ -790,9 +787,8 @@ github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J
github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w=
github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss=
github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA=
github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE=
github.com/fergusstrange/embedded-postgres v1.25.0 h1:sa+k2Ycrtz40eCRPOzI7Ry7TtkWXXJ+YRsxpKMDhxK0=
github.com/fergusstrange/embedded-postgres v1.25.0/go.mod h1:t/MLs0h9ukYM6FSt99R7InCHs1nW0ordoVCcnzmpTYw=
github.com/fergusstrange/embedded-postgres v1.10.0 h1:YnwF6xAQYmKLAXXrrRx4rHDLih47YJwVPvg8jeKfdNg=
github.com/fergusstrange/embedded-postgres v1.10.0/go.mod h1:a008U8/Rws5FtIOTGYDYa7beVWsT3qVKyqExqYYjL+c=
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
@ -800,13 +796,10 @@ github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHqu
github.com/frankban/quicktest v1.0.0/go.mod h1:R98jIehRai+d1/3Hv2//jOVCTJhW1VBavT6B6CuGq2k=
github.com/frankban/quicktest v1.2.2/go.mod h1:Qh/WofXFeiAFII1aEBu529AtJo6Zg2VHscnEsbBnJ20=
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
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=
@ -837,26 +830,23 @@ github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre
github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M=
github.com/go-pdf/fpdf v0.6.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M=
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/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=
github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0=
github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt/v4 v4.4.2 h1:rcc4lwaZgFMCZ5jxF9ABolDcIHdBytAFgqFPbSJQAYs=
github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-migrate/migrate/v4 v4.17.0 h1:rd40H3QXU0AA4IoLllFcEAEo9dYKRHYND2gB4p7xcaU=
github.com/golang-migrate/migrate/v4 v4.17.0/go.mod h1:+Cp2mtLP4/aXDTKb9wmXYitdrNx2HGs45rbWAo6OsKM=
github.com/golang-migrate/migrate/v4 v4.16.1 h1:O+0C55RbMN66pWm5MjO6mw0px6usGpY0+bkSGW9zCo0=
github.com/golang-migrate/migrate/v4 v4.16.1/go.mod h1:qXiwa/3Zeqaltm1MxOCZDYysW/F6folYiBgBG03l9hc=
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 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.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo=
github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@ -882,9 +872,9 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
@ -910,7 +900,6 @@ github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
@ -932,15 +921,14 @@ github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo=
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg=
@ -977,8 +965,6 @@ github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+l
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
@ -995,10 +981,10 @@ github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsU
github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=
github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY=
github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
github.com/jackc/pgconn v1.14.3 h1:bVoTr12EGANZz66nZPkMInAV/KHD2TxH9npjXXgiB3w=
github.com/jackc/pgconn v1.14.3/go.mod h1:RZbme4uasqzybK2RK5c65VsHxoyaml09lx3tXOcO/VM=
github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438 h1:Dj0L5fhJ9F82ZJyVOmBx6msDp/kfd1t9GRfny/mfJA0=
github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438/go.mod h1:a/s9Lp5W7n/DD0VrVoyJ00FbP2ytTPDVOivvn2bMlds=
github.com/jackc/pgconn v1.14.0 h1:vrbA9Ud87g6JdFWkHTJXppVce58qPIdP7N8y0Ml/A7Q=
github.com/jackc/pgconn v1.14.0/go.mod h1:9mBNlny0UvkgJdCDvdVHYSjI+8tD2rnKK69Wz8ti++E=
github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa h1:s+4MhCQ6YrzisK6hFJUX53drDT4UsSW3DEhKn0ifuHw=
github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa/go.mod h1:a/s9Lp5W7n/DD0VrVoyJ00FbP2ytTPDVOivvn2bMlds=
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
@ -1014,8 +1000,8 @@ github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvW
github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.3.3 h1:1HLSx5H+tXR9pW3in3zaztoEwQYRC9SQaYUHjTSUOag=
github.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.3.2 h1:7eY55bdBeCz1F2fTzSz69QC+pG46jYq9/jtSPiJ5nn0=
github.com/jackc/pgproto3/v2 v2.3.2/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
@ -1029,11 +1015,12 @@ github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
github.com/jackc/pgx/v4 v4.18.2 h1:xVpYkNR5pk5bMCZGfClbO962UIqVABcAGt7ha1s/FeU=
github.com/jackc/pgx/v4 v4.18.2/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw=
github.com/jackc/pgx/v4 v4.18.1 h1:YP7G1KABtKpB5IHrO9vYwSrCOhs7p3uqhvhhQBptya0=
github.com/jackc/pgx/v4 v4.18.1/go.mod h1:FydWkUyadDmdNH/mHnGob881GawxeEm7TcMCzkb+qQE=
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackpal/gateway v1.0.5 h1:qzXWUJfuMdlLMtt0a3Dgt+xkWQiA5itDEITVJtuSwMc=
github.com/jackpal/gateway v1.0.5/go.mod h1:lTpwd4ACLXmpyiCTRtfiNyVnUmqT9RivzCDQetPfnjA=
github.com/jackpal/go-nat-pmp v0.0.0-20170405195558-28a68d0c24ad h1:heFfj7z0pGsNCekUlsFhO2jstxO4b5iQ665LjwM5mDc=
@ -1055,27 +1042,20 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a/go.mod h1:UJSiEoRfvx3hP73CvoARgeLjaIOjybY9vj8PUPPFGeU=
github.com/juju/clock v0.0.0-20220203021603-d9deb868a28a h1:Az/6CM/P5guGHNy7r6TkOCctv3lDmN3W1uhku7QMupk=
github.com/juju/clock v0.0.0-20220203021603-d9deb868a28a/go.mod h1:GZ/FY8Cqw3KHG6DwRVPUKbSPTAwyrU28xFi5cqZnLsc=
github.com/juju/collections v0.0.0-20220203020748-febd7cad8a7a h1:d7eZO8OS/ZXxdP0uq3E8CdoA1qNFaecAv90UxrxaY2k=
github.com/juju/collections v0.0.0-20220203020748-febd7cad8a7a/go.mod h1:JWeZdyttIEbkR51z2S13+J+aCuHVe0F6meRy+P0YGDo=
github.com/juju/errors v0.0.0-20220331221717-b38fca44723b h1:AxFeSQJfcm2O3ov1wqAkTKYFsnMw2g1B4PkYujfAdkY=
github.com/juju/errors v0.0.0-20220331221717-b38fca44723b/go.mod h1:jMGj9DWF/qbo91ODcfJq6z/RYc3FX3taCBZMCcpI4Ls=
github.com/juju/loggo v0.0.0-20210728185423-eebad3a902c4 h1:NO5tuyw++EGLnz56Q8KMyDZRwJwWO8jQnj285J3FOmY=
github.com/juju/loggo v0.0.0-20210728185423-eebad3a902c4/go.mod h1:NIXFioti1SmKAlKNuUwbMenNdef59IF52+ZzuOmHYkg=
github.com/juju/mgo/v2 v2.0.0-20220111072304-f200228f1090 h1:zX5GoH3Jp8k1EjUFkApu/YZAYEn0PYQfg/U6IDyNyYs=
github.com/juju/mgo/v2 v2.0.0-20220111072304-f200228f1090/go.mod h1:N614SE0a4e+ih2rg96Vi2PeC3cTpUOWgCTv3Cgk974c=
github.com/juju/retry v0.0.0-20220204093819-62423bf33287 h1:U+7oMWEglXfiikIppNexButZRwKPlzLBGKYSNCXzXf8=
github.com/juju/retry v0.0.0-20220204093819-62423bf33287/go.mod h1:SssN1eYeK3A2qjnFGTiVMbdzGJ2BfluaJblJXvuvgqA=
github.com/juju/testing v0.0.0-20220203020004-a0ff61f03494 h1:XEDzpuZb8Ma7vLja3+5hzUqVTvAqm5Y+ygvnDs5iTMM=
github.com/juju/testing v0.0.0-20220203020004-a0ff61f03494/go.mod h1:rUquetT0ALL48LHZhyRGvjjBH8xZaZ8dFClulKK5wK4=
github.com/juju/utils/v3 v3.0.0-20220203023959-c3fbc78a33b0 h1:bn+2Adl1yWqYjm3KSFlFqsvfLg2eq+XNL7GGMYApdVw=
github.com/juju/utils/v3 v3.0.0-20220203023959-c3fbc78a33b0/go.mod h1:8csUcj1VRkfjNIRzBFWzLFCMLwLqsRWvkmhfVAUwbC4=
github.com/juju/version/v2 v2.0.0-20220204124744-fc9915e3d935 h1:6YoyzXVW1XkqN86y2s/rz365Jm7EiAy39v2G5ikzvHU=
github.com/juju/version/v2 v2.0.0-20220204124744-fc9915e3d935/go.mod h1:ZeFjNy+UFEWJDDPdzW7Cm9NeU6dsViGaFYhXzycLQrw=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
@ -1083,8 +1063,15 @@ github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6
github.com/kkdai/bstream v1.0.0 h1:Se5gHwgp2VT2uHfDrkbbgbgEvV9cimLELwrPJctSjg8=
github.com/kkdai/bstream v1.0.0/go.mod h1:FDnDOHt5Yx4p3FaHcioFT0QjDOtgUpvjeZqAs+NVZZA=
github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE=
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.10.10/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY=
github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/pgzip v1.2.4/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE=
github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
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=
@ -1094,7 +1081,6 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
@ -1103,43 +1089,41 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
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/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw=
github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lightninglabs/aperture v0.1.21-beta.0.20230705004936-87bb996a4030 h1:q/BBO2awQdy/dCILXXZbBsstQ+1DpSQ/c8B8EgKqg+g=
github.com/lightninglabs/aperture v0.1.21-beta.0.20230705004936-87bb996a4030/go.mod h1:Jvoen+fgoaGQZIHdchiGigu0Lwuwz8S5u5wad9IhVDU=
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.0-1 h1:b9ur24NTbNRUOfotkhio6SAlkvXADLz9k7QLIlLYpSk=
github.com/lightninglabs/lndclient v0.18.0-1/go.mod h1:GBIttLpj+W82XrZrFvQ1gpQH074aTcwisP/zvdGbqE4=
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/lndclient v0.17.4-1 h1:uCLBYf1f1nOoagHuiPK9anERA86dNSlYK9/QGb410RQ=
github.com/lightninglabs/lndclient v0.17.4-1/go.mod h1:2krqTDgp3W3DLSDx9bYaT0MDrMVslGMXETViKE8J1pk=
github.com/lightninglabs/neutrino v0.16.0 h1:YNTQG32fPR/Zg0vvJVI65OBH8l3U18LSXXtX91hx0q0=
github.com/lightninglabs/neutrino v0.16.0/go.mod h1:x3OmY2wsA18+Kc3TSV2QpSUewOCiscw2mKpXgZv2kZk=
github.com/lightninglabs/neutrino/cache v1.1.1 h1:TllWOSlkABhpgbWJfzsrdUaDH2fBy/54VSIB4vVqV8M=
github.com/lightninglabs/neutrino/cache v1.1.1/go.mod h1:XJNcgdOw1LQnanGjw8Vj44CvguYA25IMKjWFZczwZuo=
github.com/lightninglabs/protobuf-go-hex-display v1.30.0-hex-display h1:pRdza2wleRN1L2fJXd6ZoQ9ZegVFTAb2bOQfruJPKcY=
github.com/lightninglabs/protobuf-go-hex-display v1.30.0-hex-display/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
github.com/lightningnetwork/lightning-onion v1.2.1-0.20230823005744-06182b1d7d2f h1:Pua7+5TcFEJXIIZ1I2YAUapmbcttmLj4TTi786bIi3s=
github.com/lightningnetwork/lightning-onion v1.2.1-0.20230823005744-06182b1d7d2f/go.mod h1:c0kvRShutpj3l6B9WtTsNTBUtjSmjZXbJd9ZBRQOSKI=
github.com/lightningnetwork/lnd v0.18.0-beta.1 h1:7DpRre4rtUmLim4JC5oPd3KEd1Q3QpWTH6jQgSOGNYM=
github.com/lightningnetwork/lnd v0.18.0-beta.1/go.mod h1:1SA9iv9rZddNAcfP38SN9lNSVT1zf5aqmukLUoomjDU=
github.com/lightningnetwork/lnd v0.17.4-beta h1:BXYbETYZWtcNrYcAosGGXnWsq4Nr5R9PRqlRuEA9AUs=
github.com/lightningnetwork/lnd v0.17.4-beta/go.mod h1:S5hugoB/FWyF9Up9sjEnOsA/ohmhXzIqRHHMLlrtyFk=
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.0.1/go.mod h1:KnQudQ6w0IAMZi1SgvecLZQZ43ra2vpDNj7H/aasemg=
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.0.5 h1:ffDgMSn83avw6rNzxhbt6w5/2oIrwQKTPGfyaLupZtE=
github.com/lightningnetwork/lnd/fn v1.0.5/go.mod h1:P027+0CyELd92H9gnReUkGGAqbFA1HwjHWdfaDFD51U=
github.com/lightningnetwork/lnd/healthcheck v1.2.4 h1:lLPLac+p/TllByxGSlkCwkJlkddqMP5UCoawCj3mgFQ=
github.com/lightningnetwork/lnd/healthcheck v1.2.4/go.mod h1:G7Tst2tVvWo7cx6mSBEToQC5L1XOGxzZTPB29g9Rv2I=
github.com/lightningnetwork/lnd/kvdb v1.4.8 h1:xH0a5Vi1yrcZ5BEeF2ba3vlKBRxrL9uYXlWTjOjbNTY=
github.com/lightningnetwork/lnd/kvdb v1.4.8/go.mod h1:J2diNABOoII9UrMnxXS5w7vZwP7CA1CStrl8MnIrb3A=
github.com/lightningnetwork/lnd/healthcheck v1.2.3 h1:oqhOOy8WmIEa6RBkYKC0mmYZkhl8T2kGD97n9jpML8o=
github.com/lightningnetwork/lnd/healthcheck v1.2.3/go.mod h1:eDxH3dEwV9DeBW/6inrmlVh1qBOFV0AI14EEPnGt9gc=
github.com/lightningnetwork/lnd/kvdb v1.4.4 h1:bCv63rVCvzqj1BkagN/EWTov6NDDgYEG/t0z2HepRMk=
github.com/lightningnetwork/lnd/kvdb v1.4.4/go.mod h1:9SuaIqMA9ugrVkdvgQkYXa8CAKYNYd4vsEYORP4V698=
github.com/lightningnetwork/lnd/queue v1.1.1 h1:99ovBlpM9B0FRCGYJo6RSFDlt8/vOkQQZznVb18iNMI=
github.com/lightningnetwork/lnd/queue v1.1.1/go.mod h1:7A6nC1Qrm32FHuhx/mi1cieAiBZo5O6l8IBIoQxvkz4=
github.com/lightningnetwork/lnd/sqldb v1.0.2 h1:PfuYzScYMD9/QonKo/QvgsbXfTnH5DfldIimkfdW4Bk=
github.com/lightningnetwork/lnd/sqldb v1.0.2/go.mod h1:V2Xl6JNWLTKE97WJnwfs0d0TYJdIQTqK8/3aAwkd3qI=
github.com/lightningnetwork/lnd/ticker v1.1.1 h1:J/b6N2hibFtC7JLV77ULQp++QLtCwT6ijJlbdiZFbSM=
github.com/lightningnetwork/lnd/ticker v1.1.1/go.mod h1:waPTRAAcwtu7Ji3+3k+u/xH5GHovTsCoSVpho0KDvdA=
github.com/lightningnetwork/lnd/tlv v1.2.3 h1:If5ibokA/UoCBGuCKaY6Vn2SJU0l9uAbehCnhTZjEP8=
github.com/lightningnetwork/lnd/tlv v1.2.3/go.mod h1:zDkmqxOczP6LaLTvSFDQ1SJUfHcQRCMKFj93dn3eMB8=
github.com/lightningnetwork/lnd/tlv v1.1.1 h1:BW1u9+uHLRA9sm+8FBkAg1H9rPjrj3S9KvXYiCYjQWk=
github.com/lightningnetwork/lnd/tlv v1.1.1/go.mod h1:292dSXpZ+BNnSJFjS1qvHden9LEbulmECglSgfg+4lw=
github.com/lightningnetwork/lnd/tor v1.1.2 h1:3zv9z/EivNFaMF89v3ciBjCS7kvCj4ZFG7XvD2Qq0/k=
github.com/lightningnetwork/lnd/tor v1.1.2/go.mod h1:j7T9uJ2NLMaHwE7GiBGnpYLn4f7NRoTM6qj+ul6/ycA=
github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796 h1:sjOGyegMIhvgfq5oaue6Td+hxZuf3tDC8lAPrFldqFw=
@ -1157,14 +1141,15 @@ github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
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/mholt/archiver/v3 v3.5.0 h1:nE8gZIrw66cu4osS/U7UW7YDuGMHssxKutU8IfWxwWE=
github.com/mholt/archiver/v3 v3.5.0/go.mod h1:qqTTPUK/HZPFgFQ/TJ3BzvTpF/dPtFVJXdQbCmeMxwc=
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/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY=
@ -1181,26 +1166,23 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
github.com/nwaples/rardecode v1.1.2 h1:Cj0yZY6T1Zx1R7AhTbyGSALm44/Mmq+BAPc4B/p/d3M=
github.com/nwaples/rardecode v1.1.2/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.26.0 h1:03cDLK28U6hWvCAns6NeydX3zIm4SF3ci69ulidS32Q=
github.com/onsi/gomega v1.26.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
@ -1213,6 +1195,8 @@ github.com/ory/dockertest/v3 v3.10.0/go.mod h1:nr57ZbRWMqfsdGdFNLHz5jjNdDb7VVFnz
github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY=
github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI=
github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI=
github.com/pierrec/lz4/v4 v4.0.3/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0=
github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@ -1246,9 +1230,8 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O
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/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
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=
github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi2s=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
@ -1266,7 +1249,6 @@ github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdh
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
@ -1287,9 +1269,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
@ -1300,18 +1281,21 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY=
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 h1:uruHq4dN7GR16kFc5fp3d1RIYzJW5onx8Ybykw2YQFA=
github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tv42/zbase32 v0.0.0-20160707012821-501572607d02 h1:tcJ6OjwOMvExLlzrAVZute09ocAGa7KqOON60++Gz4E=
github.com/tv42/zbase32 v0.0.0-20160707012821-501572607d02/go.mod h1:tHlrkM198S068ZqfrO6S8HsoJq2bF3ETfTL+kt4tInY=
github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
github.com/ulikunitz/xz v0.5.7/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8=
github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/urfave/cli v1.22.9 h1:cv3/KhXGBGjEXLC4bH0sLuJ9BewaAbpk5oyMOveu4pw=
github.com/urfave/cli v1.22.9/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
@ -1334,6 +1318,7 @@ 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.5-0.20200615073812-232d8fc87f50/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
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=
@ -1358,25 +1343,24 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
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/google.golang.org/grpc/otelgrpc v0.46.0 h1:PzIubN4/sjByhDRHLviCjJuweBXWFZWhghjg7cS28+M=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.0/go.mod h1:Ct6zzQEuGK3WpJs2n4dn+wfJYzd/+hNnxMRTWjGn30M=
go.opentelemetry.io/otel v1.3.0/go.mod h1:PWIKzi6JCp7sM0k9yZ43VX+T345uNbAkDKwHVjb2PTs=
go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc=
go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo=
go.opentelemetry.io/otel v1.20.0 h1:vsb/ggIY+hUjD/zCAQHpzTmndPqv/ml2ArbsbfBYTAc=
go.opentelemetry.io/otel v1.20.0/go.mod h1:oUIGj3D77RwJdM6PPZImDpSZGDvkD9fhesHny69JFrs=
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.21.0 h1:tlYWfeo+Bocx5kLEloTjbcDwBuELRrIFxwdQ36PlJu4=
go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM=
go.opentelemetry.io/otel/metric v1.20.0 h1:ZlrO8Hu9+GAhnepmRGhSU7/VkpjrNowxRN9GyKR4wzA=
go.opentelemetry.io/otel/metric v1.20.0/go.mod h1:90DRw3nfK4D7Sm/75yQ00gTJxtkBxX+wu6YaNymbpVM=
go.opentelemetry.io/otel/sdk v1.3.0 h1:3278edCoH89MEJ0Ky8WQXVmDQv3FX4ZJ3Pp+9fJreAI=
go.opentelemetry.io/otel/sdk v1.3.0/go.mod h1:rIo4suHNhQwBIPg9axF8V9CA72Wz2mKF1teNrup8yzs=
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.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8fpfLc=
go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ=
go.opentelemetry.io/otel/trace v1.20.0 h1:+yxVAPZPbQhbC3OfAkeIVTky6iTFpcr4SiY9om7mXSQ=
go.opentelemetry.io/otel/trace v1.20.0/go.mod h1:HJSK7F/hA5RlzpZ0zKDCHCDHm556LCDtKaAo6JmBFUU=
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=
@ -1417,8 +1401,9 @@ golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@ -1434,8 +1419,8 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 h1:aAcj0Da7eBAtrTp03QXWvm88pSyOt+UgdZw2BFZ+lEw=
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ=
golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0 h1:pVgRXcIictcr+lBQIFeiwuwtDIs4eL21OuM9nyAADmo=
golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
@ -1478,8 +1463,8 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk=
golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -1546,8 +1531,8 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -1577,8 +1562,7 @@ golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec
golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I=
golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw=
golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4=
golang.org/x/oauth2 v0.14.0 h1:P0Vrf/2538nmC0H+pEQ3MNFRRnVR7RlqyVw+bvm26z0=
golang.org/x/oauth2 v0.14.0/go.mod h1:lAtNWgaWfL4cm7j2OV8TxGi9Qb7ECORx8DktCY74OwM=
golang.org/x/oauth2 v0.11.0 h1:vPL4xzxBM4niKCW6g9whtaWVXTJf1U5e4aZxxFx/gbU=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -1595,8 +1579,8 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -1695,8 +1679,8 @@ golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@ -1706,8 +1690,8 @@ golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -1801,8 +1785,8 @@ 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=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw=
golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=
golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo=
golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -2019,12 +2003,12 @@ google.golang.org/genproto v0.0.0-20230323212658-478b75c54725/go.mod h1:UUQDJDOl
google.golang.org/genproto v0.0.0-20230330154414-c0448cd141ea/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak=
google.golang.org/genproto v0.0.0-20230331144136-dcfb400f0633/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak=
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU=
google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b h1:+YaDE2r2OG8t/z5qmsh7Y+XXwCbvadxxZ0YY6mTdrVA=
google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:CgAqfJo+Xmu0GwA0411Ht3OU3OntXwsGmrmjI8ioGXI=
google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b h1:CIC2YMXmIhYw6evmhPxBKJ4fmLbOFtXQN/GV3XOZR8k=
google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:IBQ646DjkDkvUIsVq/cc03FUFQ9wbZu7yE396YcL870=
google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 h1:AB/lmRny7e2pLhFEYIbl5qkDAUt2h0ZRO4wGPhZf+ik=
google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405/go.mod h1:67X1fPuzjcrkymZzZV1vvkFeTn2Rvc6lYF9MYFGCcwE=
google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d h1:VBu5YqKPv6XiJ199exd8Br+Aetz+o08F+PLMnwJQHAY=
google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4=
google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d h1:DoPTO70H+bcDXcd39vOqb2viZxgqeBeSGtZ55yZU4/Q=
google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@ -2104,7 +2088,6 @@ gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.3.0 h1:MfDY1b1/0xN1CyMlQDac0ziEy9zJQd9CXBRRDHw2jJo=
gotest.tools/v3 v3.3.0/go.mod h1:Mcr9QNxkg0uMvy/YElmo4SpXgJKWgQvYrT7Kw5RzJ1A=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
@ -2114,27 +2097,24 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las=
lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI=
lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
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/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw=
modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0=
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/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw=
modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY=
modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk=
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=
modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw=
modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU=
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI=
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4=
modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA=
modernc.org/libc v1.16.0/go.mod h1:N4LD6DBE9cf+Dzf9buBlzVJndKr/iJHG97vGLHYnb5A=
@ -2143,35 +2123,33 @@ 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.22.2 h1:4U7v51GyhlWqQmwCHj28Rdq2Yzwk55ovjFrdPjs8Hb0=
modernc.org/libc v1.22.2/go.mod h1:uvQavJ1pZ0hIoC/jfqNoMLURIMhKzINIWypNM17puug=
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 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw=
modernc.org/memory v1.2.0/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw=
modernc.org/memory v1.2.1/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E=
modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU=
modernc.org/memory v1.4.0 h1:crykUfNSnMAXaOJnnxcSzbUGMqkLWjklJKkBK2nwZwk=
modernc.org/memory v1.4.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
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.8 h1:nGKglNx9K5v0As+zF0/Gcl1kMkmaU1XynYyq92PbsC8=
modernc.org/sqlite v1.29.8/go.mod h1:lQPm27iqa4UNZpmr4Aor0MH0HkCLbt1huYDfWylLZFk=
modernc.org/sqlite v1.20.3 h1:SqGJMMxjj1PHusLxdYxeQSodg7Jxn9WWkaAQjKrntZs=
modernc.org/sqlite v1.20.3/go.mod h1:zKcGyrICaxNTMEHSr1HQ2GUraP0j+845GYw37+EyT6A=
modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw=
modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY=
modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw=
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw=
modernc.org/tcl v1.15.0 h1:oY+JeD11qVVSgVvodMJsu7Edf8tr5E/7tuhF5cNYz34=
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/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg=
modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8=
modernc.org/z v1.7.0 h1:xkDw/KepgEjeizO2sNco+hqYkU12taxQFqPEmgm1GWE=
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=

@ -19,7 +19,6 @@ import (
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnrpc/walletrpc"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwallet"
)
const (
@ -60,7 +59,6 @@ type InitInstantOutCtx struct {
initationHeight int32
outgoingChanSet loopdb.ChannelSet
protocolVersion ProtocolVersion
sweepAddress btcutil.Address
}
// InitInstantOutAction is the first action that is executed when the instant
@ -109,7 +107,7 @@ func (f *FSM) InitInstantOutAction(eventCtx fsm.EventContext) fsm.EventType {
if int32(res.Expiry) < initCtx.cltvExpiry+htlcExpiryDelta {
return f.HandleError(fmt.Errorf("reservation %x has "+
"expiry %v which is less than the swap expiry %v",
resId, res.Expiry, initCtx.cltvExpiry+htlcExpiryDelta))
resId, res.Expiry, initCtx.cltvExpiry))
}
}
@ -167,15 +165,11 @@ func (f *FSM) InitInstantOutAction(eventCtx fsm.EventContext) fsm.EventType {
}
// Create the address that we'll send the funds to.
sweepAddress := initCtx.sweepAddress
if sweepAddress == nil {
sweepAddress, err = f.cfg.Wallet.NextAddr(
f.ctx, lnwallet.DefaultAccountName,
walletrpc.AddressType_TAPROOT_PUBKEY, false,
)
if err != nil {
return f.HandleError(err)
}
sweepAddress, err := f.cfg.Wallet.NextAddr(
f.ctx, "", walletrpc.AddressType_TAPROOT_PUBKEY, false,
)
if err != nil {
return f.HandleError(err)
}
// Now we can create the instant out.
@ -185,13 +179,13 @@ func (f *FSM) InitInstantOutAction(eventCtx fsm.EventContext) fsm.EventType {
protocolVersion: ProtocolVersionFullReservation,
initiationHeight: initCtx.initationHeight,
outgoingChanSet: initCtx.outgoingChanSet,
CltvExpiry: initCtx.cltvExpiry,
cltvExpiry: initCtx.cltvExpiry,
clientPubkey: keyRes.PubKey,
serverPubkey: serverPubkey,
Value: btcutil.Amount(reservationAmt),
value: btcutil.Amount(reservationAmt),
htlcFeeRate: feeRate,
swapInvoice: instantOutResponse.SwapInvoice,
Reservations: reservations,
reservations: reservations,
keyLocator: keyRes.KeyLocator,
sweepAddress: sweepAddress,
}
@ -211,7 +205,7 @@ func (f *FSM) InitInstantOutAction(eventCtx fsm.EventContext) fsm.EventType {
func (f *FSM) PollPaymentAcceptedAction(_ fsm.EventContext) fsm.EventType {
// Now that we're doing the swap, we first lock the reservations
// so that they can't be used for other swaps.
for _, reservation := range f.InstantOut.Reservations {
for _, reservation := range f.InstantOut.reservations {
err := f.cfg.ReservationManager.LockReservation(
f.ctx, reservation.ID,
)
@ -227,7 +221,7 @@ func (f *FSM) PollPaymentAcceptedAction(_ fsm.EventContext) fsm.EventType {
Invoice: f.InstantOut.swapInvoice,
Timeout: defaultSendpaymentTimeout,
MaxParts: defaultMaxParts,
MaxFee: getMaxRoutingFee(f.InstantOut.Value),
MaxFee: getMaxRoutingFee(f.InstantOut.value),
},
)
if err != nil {
@ -301,7 +295,7 @@ func (f *FSM) BuildHTLCAction(eventCtx fsm.EventContext) fsm.EventType {
return f.handleErrorAndUnlockReservations(err)
}
if len(htlcInitRes.HtlcServerNonces) != len(f.InstantOut.Reservations) {
if len(htlcInitRes.HtlcServerNonces) != len(f.InstantOut.reservations) {
return f.handleErrorAndUnlockReservations(
errors.New("invalid number of server nonces"),
)
@ -435,8 +429,8 @@ func (f *FSM) PushPreimageAction(eventCtx fsm.EventContext) fsm.EventType {
return OnErrorPublishHtlc
}
f.InstantOut.FinalizedSweeplessSweepTx = sweepTx
txHash := f.InstantOut.FinalizedSweeplessSweepTx.TxHash()
f.InstantOut.finalizedSweeplessSweepTx = sweepTx
txHash := f.InstantOut.finalizedSweeplessSweepTx.TxHash()
f.InstantOut.SweepTxHash = &txHash
@ -598,7 +592,7 @@ func (f *FSM) handleErrorAndUnlockReservations(err error) fsm.EventType {
defer cancel()
// Unlock the reservations.
for _, reservation := range f.InstantOut.Reservations {
for _, reservation := range f.InstantOut.reservations {
err := f.cfg.ReservationManager.UnlockReservation(
ctx, reservation.ID,
)

@ -35,16 +35,16 @@ type InstantOut struct {
// State is the current state of the swap.
State fsm.StateType
// CltvExpiry is the expiry of the swap.
CltvExpiry int32
// cltvExpiry is the expiry of the swap.
cltvExpiry int32
// outgoingChanSet optionally specifies the short channel ids of the
// channels that may be used to loop out.
outgoingChanSet loopdb.ChannelSet
// Reservations are the Reservations that are used in as inputs for the
// reservations are the reservations that are used in as inputs for the
// instant out swap.
Reservations []*reservation.Reservation
reservations []*reservation.Reservation
// protocolVersion is the version of the protocol that is used for the
// swap.
@ -53,8 +53,8 @@ type InstantOut struct {
// initiationHeight is the height at which the swap was initiated.
initiationHeight int32
// Value is the amount that is swapped.
Value btcutil.Amount
// value is the amount that is swapped.
value btcutil.Amount
// keyLocator is the key locator that is used for the swap.
keyLocator keychain.KeyLocator
@ -81,9 +81,9 @@ type InstantOut struct {
// SweepTxHash is the hash of the sweep transaction.
SweepTxHash *chainhash.Hash
// FinalizedSweeplessSweepTx is the transaction that is used to sweep
// finalizedSweeplessSweepTx is the transaction that is used to sweep
// the funds in the cooperative path.
FinalizedSweeplessSweepTx *wire.MsgTx
finalizedSweeplessSweepTx *wire.MsgTx
// sweepConfirmationHeight is the height at which the sweep
// transaction was confirmed.
@ -93,7 +93,7 @@ type InstantOut struct {
// getHtlc returns the swap.htlc for the instant out.
func (i *InstantOut) getHtlc(chainParams *chaincfg.Params) (*swap.Htlc, error) {
return swap.NewHtlcV2(
i.CltvExpiry, pubkeyTo33ByteSlice(i.serverPubkey),
i.cltvExpiry, pubkeyTo33ByteSlice(i.serverPubkey),
pubkeyTo33ByteSlice(i.clientPubkey), i.SwapHash, chainParams,
)
}
@ -104,11 +104,11 @@ func (i *InstantOut) createMusig2Session(ctx context.Context,
[][]byte, error) {
// Create the htlc musig2 context.
musig2Sessions := make([]*input.MuSig2SessionInfo, len(i.Reservations))
clientNonces := make([][]byte, len(i.Reservations))
musig2Sessions := make([]*input.MuSig2SessionInfo, len(i.reservations))
clientNonces := make([][]byte, len(i.reservations))
// Create the sessions and nonces from the reservations.
for idx, reservation := range i.Reservations {
for idx, reservation := range i.reservations {
session, err := reservation.Musig2CreateSession(ctx, signer)
if err != nil {
return nil, nil, err
@ -123,12 +123,12 @@ func (i *InstantOut) createMusig2Session(ctx context.Context,
// getInputReservation returns the input reservation for the instant out.
func (i *InstantOut) getInputReservations() (InputReservations, error) {
if len(i.Reservations) == 0 {
if len(i.reservations) == 0 {
return nil, errors.New("no reservations")
}
inputs := make(InputReservations, len(i.Reservations))
for idx, reservation := range i.Reservations {
inputs := make(InputReservations, len(i.reservations))
for idx, reservation := range i.reservations {
pkScript, err := reservation.GetPkScript()
if err != nil {
return nil, err
@ -170,7 +170,7 @@ func (i *InstantOut) createHtlcTransaction(network *chaincfg.Params) (
// Estimate the fee
weight := htlcWeight(len(inputReservations))
fee := i.htlcFeeRate.FeeForWeight(weight)
if fee > i.Value/5 {
if fee > i.value/5 {
return nil, errors.New("fee is higher than 20% of " +
"sweep value")
}
@ -182,7 +182,7 @@ func (i *InstantOut) createHtlcTransaction(network *chaincfg.Params) (
// Create the sweep output
sweepOutput := &wire.TxOut{
Value: int64(i.Value) - int64(fee),
Value: int64(i.value) - int64(fee),
PkScript: htlc.PkScript,
}
@ -214,7 +214,7 @@ func (i *InstantOut) createSweeplessSweepTx(feerate chainfee.SatPerKWeight) (
// Estimate the fee
weight := sweeplessSweepWeight(len(inputReservations))
fee := feerate.FeeForWeight(weight)
if fee > i.Value/5 {
if fee > i.value/5 {
return nil, errors.New("fee is higher than 20% of " +
"sweep value")
}
@ -226,7 +226,7 @@ func (i *InstantOut) createSweeplessSweepTx(feerate chainfee.SatPerKWeight) (
// Create the sweep output
sweepOutput := &wire.TxOut{
Value: int64(i.Value) - int64(fee),
Value: int64(i.value) - int64(fee),
PkScript: pkscript,
}
@ -381,7 +381,7 @@ func (i *InstantOut) generateHtlcSweepTx(ctx context.Context,
return nil, err
}
fee := feeRate.FeeForWeight(weightEstimator.Weight())
fee := feeRate.FeeForWeight(int64(weightEstimator.Weight()))
htlcOutValue := i.finalizedHtlcTx.TxOut[0].Value
output := &wire.TxOut{
@ -424,7 +424,7 @@ func (i *InstantOut) generateHtlcSweepTx(ctx context.Context,
}
// htlcWeight returns the weight for the htlc transaction.
func htlcWeight(numInputs int) lntypes.WeightUnit {
func htlcWeight(numInputs int) int64 {
var weightEstimator input.TxWeightEstimator
for i := 0; i < numInputs; i++ {
weightEstimator.AddTaprootKeySpendInput(
@ -434,11 +434,11 @@ func htlcWeight(numInputs int) lntypes.WeightUnit {
weightEstimator.AddP2WSHOutput()
return weightEstimator.Weight()
return int64(weightEstimator.Weight())
}
// sweeplessSweepWeight returns the weight for the sweepless sweep transaction.
func sweeplessSweepWeight(numInputs int) lntypes.WeightUnit {
func sweeplessSweepWeight(numInputs int) int64 {
var weightEstimator input.TxWeightEstimator
for i := 0; i < numInputs; i++ {
weightEstimator.AddTaprootKeySpendInput(
@ -448,7 +448,7 @@ func sweeplessSweepWeight(numInputs int) lntypes.WeightUnit {
weightEstimator.AddP2TROutput()
return weightEstimator.Weight()
return int64(weightEstimator.Weight())
}
// pubkeyTo33ByteSlice converts a pubkey to a 33 byte slice.

@ -137,20 +137,7 @@ func (m *Manager) recoverInstantOuts(ctx context.Context) error {
// NewInstantOut creates a new instantout.
func (m *Manager) NewInstantOut(ctx context.Context,
reservations []reservation.ID, sweepAddress string) (*FSM, error) {
var (
sweepAddr btcutil.Address
err error
)
if sweepAddress != "" {
sweepAddr, err = btcutil.DecodeAddress(
sweepAddress, m.cfg.Network,
)
if err != nil {
return nil, err
}
}
reservations []reservation.ID) (*FSM, error) {
m.Lock()
// Create the instantout request.
@ -159,7 +146,6 @@ func (m *Manager) NewInstantOut(ctx context.Context,
reservations: reservations,
initationHeight: m.currentHeight,
protocolVersion: CurrentProtocolVersion(),
sweepAddress: sweepAddr,
}
instantOut, err := NewFSM(
@ -258,8 +244,3 @@ func (m *Manager) GetInstantOutQuote(ctx context.Context,
OnChainFee: chainFee,
}, nil
}
// ListInstantOuts returns all instant outs from the database.
func (m *Manager) ListInstantOuts(ctx context.Context) ([]*InstantOut, error) {
return m.cfg.Store.ListInstantLoopOuts(ctx)
}

@ -9,6 +9,7 @@ import (
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcutil"
"github.com/lightninglabs/loop/fsm"
reservationrpc "github.com/lightninglabs/loop/swapserverrpc"
)
@ -185,7 +186,7 @@ func (m *Manager) fetchL402(ctx context.Context) {
func (m *Manager) RegisterReservationNotifications(
reservationChan chan *reservationrpc.ServerReservationNotification) error {
// In order to create a valid l402 we first are going to call
// In order to create a valid lsat we first are going to call
// the FetchL402 method. As a client might not have outbound capacity
// yet, we'll retry until we get a valid response.
if !m.hasL402 {

@ -94,8 +94,8 @@ func (s *SQLStore) CreateInstantLoopOut(ctx context.Context,
SwapHash: instantOut.SwapHash[:],
Preimage: instantOut.swapPreimage[:],
InitiationTime: s.clock.Now(),
AmountRequested: int64(instantOut.Value),
CltvExpiry: instantOut.CltvExpiry,
AmountRequested: int64(instantOut.value),
CltvExpiry: instantOut.cltvExpiry,
MaxMinerFee: 0,
MaxSwapFee: 0,
InitiationHeight: instantOut.initiationHeight,
@ -114,7 +114,7 @@ func (s *SQLStore) CreateInstantLoopOut(ctx context.Context,
}
reservationIdByteSlice := reservationIdsToByteSlice(
instantOut.Reservations,
instantOut.reservations,
)
instantOutArgs := sqlc.InsertInstantOutParams{
SwapHash: instantOut.SwapHash[:],
@ -172,9 +172,9 @@ func (s *SQLStore) UpdateInstantLoopOut(ctx context.Context,
}
var finalSweeplessSweepTx []byte
if instantOut.FinalizedSweeplessSweepTx != nil {
if instantOut.finalizedSweeplessSweepTx != nil {
var buffer bytes.Buffer
err := instantOut.FinalizedSweeplessSweepTx.Serialize(
err := instantOut.finalizedSweeplessSweepTx.Serialize(
&buffer,
)
if err != nil {
@ -355,12 +355,12 @@ func (s *SQLStore) sqlInstantOutToInstantOut(ctx context.Context,
instantOut := &InstantOut{
SwapHash: swapHash,
swapPreimage: swapPreImage,
CltvExpiry: row.CltvExpiry,
cltvExpiry: row.CltvExpiry,
outgoingChanSet: outgoingChanSet,
Reservations: reservations,
reservations: reservations,
protocolVersion: ProtocolVersion(row.ProtocolVersion),
initiationHeight: row.InitiationHeight,
Value: btcutil.Amount(row.AmountRequested),
value: btcutil.Amount(row.AmountRequested),
keyLocator: keychain.KeyLocator{
Family: keychain.KeyFamily(row.ClientKeyFamily),
Index: uint32(row.ClientKeyIndex),
@ -372,7 +372,7 @@ func (s *SQLStore) sqlInstantOutToInstantOut(ctx context.Context,
sweepAddress: sweepAddress,
finalizedHtlcTx: finalizedHtlcTx,
SweepTxHash: sweepTxHash,
FinalizedSweeplessSweepTx: finalizedSweepLessSweepTx,
finalizedSweeplessSweepTx: finalizedSweepLessSweepTx,
sweepConfirmationHeight: uint32(deserializeNullInt32(
row.SweepConfirmationHeight,
)),

@ -92,12 +92,6 @@ type OutRequest struct {
// initiated the swap (loop CLI, autolooper, LiT UI and so on) and is
// appended to the user agent string.
Initiator string
// PaymentTimeout specifies the payment timeout for the individual
// off-chain payments. As the swap payment may be retried (depending on
// the configured maximum payment timeout) the total time spent may be
// a multiple of this value.
PaymentTimeout time.Duration
}
// Out contains the full details of a loop out request. This includes things

@ -312,26 +312,9 @@ func (c *autoloopTestCtx) autoloop(step *autoloopStep) {
// Assert that we query the server for a quote for each of our
// recommended swaps. Note that this differs from our set of expected
// swaps because we may get quotes for suggested swaps but then just
// log them. The order in c.quoteRequestIn is not deterministic,
// it depends on the order of map traversal (map peerChannels in
// method Manager.SuggestSwaps). So receive from the channel an item
// and then find a corresponding expected item, using amount as a key.
amt2expected := make(map[btcutil.Amount]quoteInRequestResp)
// log them.
for _, expected := range step.quotesIn {
// Make sure all amounts are unique.
require.NotContains(c.t, amt2expected, expected.request.Amount)
amt2expected[expected.request.Amount] = expected
}
for i := 0; i < len(step.quotesIn); i++ {
request := <-c.quoteRequestIn
// Get the expected item, using amount as a key.
expected, has := amt2expected[request.Amount]
require.True(c.t, has)
delete(amt2expected, request.Amount)
assert.Equal(
c.t, expected.request.Amount, request.Amount,
)

@ -46,7 +46,6 @@ import (
"github.com/lightninglabs/loop"
"github.com/lightninglabs/loop/labels"
"github.com/lightninglabs/loop/loopdb"
clientrpc "github.com/lightninglabs/loop/looprpc"
"github.com/lightninglabs/loop/swap"
"github.com/lightningnetwork/lnd/clock"
"github.com/lightningnetwork/lnd/funding"
@ -56,6 +55,8 @@ import (
"github.com/lightningnetwork/lnd/routing/route"
"github.com/lightningnetwork/lnd/ticker"
"google.golang.org/protobuf/proto"
clientrpc "github.com/lightninglabs/loop/looprpc"
)
const (

@ -77,6 +77,7 @@ func loopInSweepFee(fee chainfee.SatPerKWeight) btcutil.Amount {
maxSize := htlc.MaxTimeoutWitnessSize()
estimator.AddWitnessInput(maxSize)
weight := int64(estimator.Weight())
return fee.FeeForWeight(estimator.Weight())
return fee.FeeForWeight(weight)
}

@ -8,12 +8,13 @@ import (
"github.com/btcsuite/btcd/btcutil"
"github.com/lightninglabs/lndclient"
clientrpc "github.com/lightninglabs/loop/looprpc"
"github.com/lightninglabs/loop/swap"
"github.com/lightningnetwork/lnd/lnrpc/walletrpc"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing/route"
clientrpc "github.com/lightninglabs/loop/looprpc"
)
var (

@ -177,7 +177,7 @@ func TestCalculateAmount(t *testing.T) {
}
}
// TestSuggestSwap tests swap suggestions for the threshold rule. It does not
// TestSuggestSwaps tests swap suggestions for the threshold rule. It does not
// many different values because we have separate tests for swap amount
// calculation.
func TestSuggestSwap(t *testing.T) {

@ -10,7 +10,7 @@ import (
"time"
"github.com/btcsuite/btcd/btcutil"
"github.com/lightninglabs/aperture/l402"
"github.com/lightninglabs/aperture/lsat"
"github.com/lightninglabs/loop/loopdb"
"github.com/lightningnetwork/lnd/cert"
"github.com/lightningnetwork/lnd/lncfg"
@ -76,10 +76,6 @@ var (
defaultLndMacaroon,
)
// DefaultLndRPCTimeout is the default timeout to use when communicating
// with lnd.
DefaultLndRPCTimeout = time.Minute
// DefaultTLSCertPath is the default full path of the autogenerated TLS
// certificate.
DefaultTLSCertPath = filepath.Join(
@ -122,9 +118,6 @@ type lndConfig struct {
MacaroonPath string `long:"macaroonpath" description:"The full path to the single macaroon to use, either the admin.macaroon or a custom baked one. Cannot be specified at the same time as macaroondir. A custom macaroon must contain ALL permissions required for all subservers to work, otherwise permission errors will occur."`
TLSPath string `long:"tlspath" description:"Path to lnd tls certificate"`
// RPCTimeout is the timeout to use when communicating with lnd.
RPCTimeout time.Duration `long:"rpctimeout" description:"The timeout to use when communicating with lnd"`
}
type loopServerConfig struct {
@ -167,10 +160,8 @@ type Config struct {
MaxLogFileSize int `long:"maxlogfilesize" description:"Maximum logfile size in MB."`
DebugLevel string `long:"debuglevel" description:"Logging level for all subsystems {trace, debug, info, warn, error, critical} -- You may also specify <subsystem>=<level>,<subsystem2>=<level>,... to set the log level for individual subsystems -- Use show to list available subsystems"`
MaxLSATCost uint32 `long:"maxlsatcost" hidden:"true"`
MaxLSATFee uint32 `long:"maxlsatfee" hidden:"true"`
MaxL402Cost uint32 `long:"maxl402cost" description:"Maximum cost in satoshis that loopd is going to pay for an L402 token automatically. Does not include routing fees."`
MaxL402Fee uint32 `long:"maxl402fee" description:"Maximum routing fee in satoshis that we are willing to pay while paying for an L402 token."`
MaxLSATCost uint32 `long:"maxlsatcost" description:"Maximum cost in satoshis that loopd is going to pay for an LSAT token automatically. Does not include routing fees."`
MaxLSATFee uint32 `long:"maxlsatfee" description:"Maximum routing fee in satoshis that we are willing to pay while paying for an LSAT token."`
LoopOutMaxParts uint32 `long:"loopoutmaxparts" description:"The maximum number of payment parts that may be used for a loop out swap."`
@ -215,8 +206,8 @@ func DefaultConfig() Config {
TLSKeyPath: DefaultTLSKeyPath,
TLSValidity: DefaultAutogenValidity,
MacaroonPath: DefaultMacaroonPath,
MaxL402Cost: l402.DefaultMaxCostSats,
MaxL402Fee: l402.DefaultMaxRoutingFeeSats,
MaxLSATCost: lsat.DefaultMaxCostSats,
MaxLSATFee: lsat.DefaultMaxRoutingFeeSats,
LoopOutMaxParts: defaultLoopOutMaxParts,
TotalPaymentTimeout: defaultTotalPaymentTimeout,
MaxPaymentRetries: defaultMaxPaymentRetries,
@ -224,7 +215,6 @@ func DefaultConfig() Config {
Lnd: &lndConfig{
Host: "localhost:10009",
MacaroonPath: DefaultLndMacaroonPath,
RPCTimeout: DefaultLndRPCTimeout,
},
}
}

@ -17,12 +17,14 @@ import (
"github.com/lightninglabs/lndclient"
"github.com/lightninglabs/loop"
"github.com/lightninglabs/loop/instantout"
"github.com/lightninglabs/loop/instantout/reservation"
"github.com/lightninglabs/loop/loopd/perms"
"github.com/lightninglabs/loop/loopdb"
"github.com/lightninglabs/loop/sweepbatcher"
"github.com/lightninglabs/loop/instantout/reservation"
loop_looprpc "github.com/lightninglabs/loop/looprpc"
loop_swaprpc "github.com/lightninglabs/loop/swapserverrpc"
"github.com/lightninglabs/loop/sweepbatcher"
"github.com/lightningnetwork/lnd/clock"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/macaroons"
@ -51,7 +53,7 @@ type ListenerCfg struct {
// on the passed TLS configuration.
restListener func(*tls.Config) (net.Listener, error)
// getLnd returns a grpc connection to a lnd instance.
// getLnd returns a grpc connection to an lnd instance.
getLnd func(lndclient.Network, *lndConfig) (*lndclient.GrpcLndServices,
error)
}
@ -118,9 +120,9 @@ func New(config *Config, lisCfg *ListenerCfg) *Daemon {
// Start starts loopd in daemon mode. It will listen for grpc connections,
// execute commands and pass back swap status information.
func (d *Daemon) Start() error {
// There should be no reason to start the daemon twice. Therefore,
// return an error if that's tried. This is mostly to guard against
// Start and StartAsSubserver both being called.
// There should be no reason to start the daemon twice. Therefore return
// an error if that's tried. This is mostly to guard against Start and
// StartAsSubserver both being called.
if atomic.AddInt32(&d.started, 1) != 1 {
return errOnlyStartOnce
}
@ -135,7 +137,7 @@ func (d *Daemon) Start() error {
// 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,
// and error handlers. If this fails, then nothing has been started yet
// and we can just return the error.
err = d.initialize(true)
if errors.Is(err, bbolt.ErrTimeout) {
@ -322,7 +324,7 @@ func (d *Daemon) startWebServers() error {
err := d.restServer.Serve(d.restListener)
// ErrServerClosed is always returned when the proxy is
// shut down, so don't log it.
if err != nil && !errors.Is(err, http.ErrServerClosed) {
if err != nil && err != http.ErrServerClosed {
// Notify the main error handler goroutine that
// we exited unexpectedly here. We don't have to
// worry about blocking as the internal error
@ -341,7 +343,7 @@ func (d *Daemon) startWebServers() error {
log.Infof("RPC server listening on %s", d.grpcListener.Addr())
err = d.grpcServer.Serve(d.grpcListener)
if err != nil && !errors.Is(err, grpc.ErrServerStopped) {
if err != nil && err != grpc.ErrServerStopped {
// Notify the main error handler goroutine that
// we exited unexpectedly here. We don't have to
// worry about blocking as the internal error
@ -409,14 +411,6 @@ func (d *Daemon) initialize(withMacaroonService bool) error {
return err
}
// Run the costs migration.
err = loop.MigrateLoopOutCosts(d.mainCtx, d.lnd.LndServices, swapDb)
if err != nil {
log.Errorf("Cost migration failed: %v", err)
return err
}
sweeperDb := sweepbatcher.NewSQLStore(baseDb, chainParams)
// Create an instance of the loop client library.
@ -688,9 +682,9 @@ func (d *Daemon) initialize(withMacaroonService bool) error {
var runtimeErr error
// There are only two ways this goroutine can exit. Either there
// is an internal error or the caller requests a shutdown.
// In both cases we wait for the stop to complete before we
// signal the caller that we're done.
// is an internal error or the caller requests shutdown. In both
// cases we wait for the stop to complete before we signal the
// caller that we're done.
select {
case runtimeErr = <-d.internalErrChan:
log.Errorf("Runtime error in daemon, shutting down: "+
@ -699,7 +693,7 @@ func (d *Daemon) initialize(withMacaroonService bool) error {
case <-d.quit:
}
// We need to shut down before sending the error on the channel,
// We need to shutdown before sending the error on the channel,
// otherwise a caller might exit the process too early.
d.stop()
cleanupMacaroonStore()
@ -730,7 +724,7 @@ func (d *Daemon) stop() {
d.mainCtxCancel()
}
// As there is no swap activity anymore, we can forcefully shut down the
// As there is no swap activity anymore, we can forcefully shutdown the
// gRPC and HTTP servers now.
log.Infof("Stopping gRPC server")
if d.grpcServer != nil {

@ -2,7 +2,7 @@ package loopd
import (
"github.com/btcsuite/btclog"
"github.com/lightninglabs/aperture/l402"
"github.com/lightninglabs/aperture/lsat"
"github.com/lightninglabs/lndclient"
"github.com/lightninglabs/loop"
"github.com/lightninglabs/loop/fsm"
@ -37,7 +37,7 @@ func SetupLoggers(root *build.RotatingLogWriter, intercept signal.Interceptor) {
lnd.AddSubLogger(root, "SWEEP", intercept, sweepbatcher.UseLogger)
lnd.AddSubLogger(root, "LNDC", intercept, lndclient.UseLogger)
lnd.AddSubLogger(root, "STORE", intercept, loopdb.UseLogger)
lnd.AddSubLogger(root, l402.Subsystem, intercept, l402.UseLogger)
lnd.AddSubLogger(root, lsat.Subsystem, intercept, lsat.UseLogger)
lnd.AddSubLogger(
root, liquidity.Subsystem, intercept, liquidity.UseLogger,
)

@ -69,10 +69,6 @@ var RequiredPermissions = map[string][]bakery.Op{
Entity: "loop",
Action: "in",
}},
"/looprpc.SwapClient/GetL402Tokens": {{
Entity: "auth",
Action: "read",
}},
"/looprpc.SwapClient/GetLsatTokens": {{
Entity: "auth",
Action: "read",
@ -112,8 +108,4 @@ var RequiredPermissions = map[string][]bakery.Op{
Entity: "swap",
Action: "read",
}},
"/looprpc.SwapClient/ListInstantOuts": {{
Entity: "swap",
Action: "read",
}},
}

@ -96,7 +96,6 @@ func NewListenerConfig(config *Config, rpcCfg RPCConfig) *ListenerCfg {
BlockUntilChainSynced: true,
CallerCtx: callerCtx,
BlockUntilUnlocked: true,
RPCTimeout: cfg.RPCTimeout,
}
// If a custom lnd connection is specified we use that

@ -15,7 +15,7 @@ import (
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg"
"github.com/lightninglabs/aperture/l402"
"github.com/lightninglabs/aperture/lsat"
"github.com/lightninglabs/lndclient"
"github.com/lightninglabs/loop"
"github.com/lightninglabs/loop/instantout"
@ -101,17 +101,6 @@ func (s *swapClientServer) LoopOut(ctx context.Context,
log.Infof("Loop out request received")
// Note that LoopOutRequest.PaymentTimeout is unsigned and therefore
// cannot be negative.
paymentTimeout := time.Duration(in.PaymentTimeout) * time.Second
// Make sure we don't exceed the total allowed payment timeout.
if paymentTimeout > s.config.TotalPaymentTimeout {
return nil, fmt.Errorf("payment timeout %v exceeds maximum "+
"allowed timeout of %v", paymentTimeout,
s.config.TotalPaymentTimeout)
}
var sweepAddr btcutil.Address
var isExternalAddr bool
var err error
@ -195,7 +184,6 @@ func (s *swapClientServer) LoopOut(ctx context.Context,
SwapPublicationDeadline: publicationDeadline,
Label: in.Label,
Initiator: in.Initiator,
PaymentTimeout: paymentTimeout,
}
switch {
@ -932,18 +920,18 @@ func (s *swapClientServer) LoopIn(ctx context.Context,
return response, nil
}
// GetL402Tokens returns all tokens that are contained in the L402 token store.
func (s *swapClientServer) GetL402Tokens(ctx context.Context,
// GetLsatTokens returns all tokens that are contained in the LSAT token store.
func (s *swapClientServer) GetLsatTokens(ctx context.Context,
_ *clientrpc.TokensRequest) (*clientrpc.TokensResponse, error) {
log.Infof("Get L402 tokens request received")
log.Infof("Get LSAT tokens request received")
tokens, err := s.impl.L402Store.AllTokens()
tokens, err := s.impl.LsatStore.AllTokens()
if err != nil {
return nil, err
}
rpcTokens := make([]*clientrpc.L402Token, len(tokens))
rpcTokens := make([]*clientrpc.LsatToken, len(tokens))
idx := 0
for key, token := range tokens {
macBytes, err := token.BaseMacaroon().MarshalBinary()
@ -951,13 +939,13 @@ func (s *swapClientServer) GetL402Tokens(ctx context.Context,
return nil, err
}
id, err := l402.DecodeIdentifier(
id, err := lsat.DecodeIdentifier(
bytes.NewReader(token.BaseMacaroon().Id()),
)
if err != nil {
return nil, err
}
rpcTokens[idx] = &clientrpc.L402Token{
rpcTokens[idx] = &clientrpc.LsatToken{
BaseMacaroon: macBytes,
PaymentHash: token.PaymentHash[:],
PaymentPreimage: token.Preimage[:],
@ -976,21 +964,6 @@ func (s *swapClientServer) GetL402Tokens(ctx context.Context,
return &clientrpc.TokensResponse{Tokens: rpcTokens}, nil
}
// GetLsatTokens returns all tokens that are contained in the L402 token store.
// Deprecated: use GetL402Tokens.
// This API is provided to maintain backward compatibility with gRPC clients
// (e.g. `loop listauth`, Terminal Web, RTL).
// Type LsatToken used by GetLsatTokens in the past was renamed to L402Token,
// but this does not affect binary encoding, so we can use type L402Token here.
func (s *swapClientServer) GetLsatTokens(ctx context.Context,
req *clientrpc.TokensRequest) (*clientrpc.TokensResponse, error) {
log.Warnf("Received deprecated call GetLsatTokens. Please update the " +
"client software. Calling GetL402Tokens now.")
return s.GetL402Tokens(ctx, req)
}
// GetInfo returns basic information about the loop daemon and details to swaps
// from the swap store.
func (s *swapClientServer) GetInfo(ctx context.Context,
@ -1221,7 +1194,7 @@ func (s *swapClientServer) InstantOut(ctx context.Context,
}
instantOutFsm, err := s.instantOutManager.NewInstantOut(
ctx, reservationIds, req.DestAddr,
ctx, reservationIds,
)
if err != nil {
return nil, err
@ -1258,47 +1231,6 @@ func (s *swapClientServer) InstantOutQuote(ctx context.Context,
}, nil
}
// ListInstantOuts returns a list of all currently known instant out swaps and
// their current status.
func (s *swapClientServer) ListInstantOuts(ctx context.Context,
_ *clientrpc.ListInstantOutsRequest) (
*clientrpc.ListInstantOutsResponse, error) {
instantOuts, err := s.instantOutManager.ListInstantOuts(ctx)
if err != nil {
return nil, err
}
rpcSwaps := make([]*clientrpc.InstantOut, 0, len(instantOuts))
for _, instantOut := range instantOuts {
rpcSwaps = append(rpcSwaps, rpcInstantOut(instantOut))
}
return &clientrpc.ListInstantOutsResponse{
Swaps: rpcSwaps,
}, nil
}
func rpcInstantOut(instantOut *instantout.InstantOut) *clientrpc.InstantOut {
var sweepTxId string
if instantOut.SweepTxHash != nil {
sweepTxId = instantOut.SweepTxHash.String()
}
reservations := make([][]byte, len(instantOut.Reservations))
for i, res := range instantOut.Reservations {
reservations[i] = res.ID[:]
}
return &clientrpc.InstantOut{
SwapHash: instantOut.SwapHash[:],
State: string(instantOut.State),
Amount: uint64(instantOut.Value),
SweepTxId: sweepTxId,
ReservationIds: reservations,
}
}
func rpcAutoloopReason(reason liquidity.Reason) (clientrpc.AutoReason, error) {
switch reason {
case liquidity.ReasonNone:

@ -6,7 +6,6 @@ import (
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg"
"github.com/lightninglabs/aperture/l402"
"github.com/lightninglabs/lndclient"
"github.com/lightninglabs/loop"
"github.com/lightninglabs/loop/liquidity"
@ -22,47 +21,19 @@ func getClient(cfg *Config, swapDb loopdb.SwapStore,
sweeperDb sweepbatcher.BatcherStore, lnd *lndclient.LndServices) (
*loop.Client, func(), error) {
// Default is not set for MaxLSATCost and MaxLSATFee to distinguish
// it from user explicitly setting the option to default value.
// So if MaxL402Cost and MaxLSATFee are not set in the config file
// and command line, they are set to 0.
const (
defaultCost = l402.DefaultMaxCostSats
defaultFee = l402.DefaultMaxRoutingFeeSats
)
if cfg.MaxL402Cost != defaultCost && cfg.MaxLSATCost != 0 {
return nil, nil, fmt.Errorf("both maxl402cost and maxlsatcost" +
" were specified; they are not allowed together")
}
if cfg.MaxL402Fee != defaultFee && cfg.MaxLSATFee != 0 {
return nil, nil, fmt.Errorf("both maxl402fee and maxlsatfee" +
" were specified; they are not allowed together")
}
clientConfig := &loop.ClientConfig{
ServerAddress: cfg.Server.Host,
ProxyAddress: cfg.Server.Proxy,
SwapServerNoTLS: cfg.Server.NoTLS,
TLSPathServer: cfg.Server.TLSPath,
Lnd: lnd,
MaxL402Cost: btcutil.Amount(cfg.MaxL402Cost),
MaxL402Fee: btcutil.Amount(cfg.MaxL402Fee),
MaxLsatCost: btcutil.Amount(cfg.MaxLSATCost),
MaxLsatFee: btcutil.Amount(cfg.MaxLSATFee),
LoopOutMaxParts: cfg.LoopOutMaxParts,
TotalPaymentTimeout: cfg.TotalPaymentTimeout,
MaxPaymentRetries: cfg.MaxPaymentRetries,
}
if cfg.MaxL402Cost == defaultCost && cfg.MaxLSATCost != 0 {
log.Warnf("Option maxlsatcost is deprecated and will be " +
"removed. Switch to maxl402cost.")
clientConfig.MaxL402Cost = btcutil.Amount(cfg.MaxLSATCost)
}
if cfg.MaxL402Fee == defaultFee && cfg.MaxLSATFee != 0 {
log.Warnf("Option maxlsatfee is deprecated and will be " +
"removed. Switch to maxl402fee.")
clientConfig.MaxL402Fee = btcutil.Amount(cfg.MaxLSATFee)
}
swapClient, cleanUp, err := loop.NewClient(
cfg.DataDir, swapDb, sweeperDb, clientConfig,
)
@ -85,8 +56,9 @@ func openDatabase(cfg *Config, chainParams *chaincfg.Params) (loopdb.SwapStore,
case DatabaseBackendSqlite:
log.Infof("Opening sqlite3 database at: %v",
cfg.Sqlite.DatabaseFileName)
db, err = loopdb.NewSqliteStore(cfg.Sqlite, chainParams)
db, err = loopdb.NewSqliteStore(
cfg.Sqlite, chainParams,
)
if err != nil {
return nil, nil, err
}
@ -95,8 +67,9 @@ func openDatabase(cfg *Config, chainParams *chaincfg.Params) (loopdb.SwapStore,
case DatabaseBackendPostgres:
log.Infof("Opening postgres database at: %v",
cfg.Postgres.DSN(true))
db, err = loopdb.NewPostgresStore(cfg.Postgres, chainParams)
db, err = loopdb.NewPostgresStore(
cfg.Postgres, chainParams,
)
if err != nil {
return nil, nil, err
}
@ -106,6 +79,9 @@ func openDatabase(cfg *Config, chainParams *chaincfg.Params) (loopdb.SwapStore,
return nil, nil, fmt.Errorf("unknown database backend: %s",
cfg.DatabaseBackend)
}
if err != nil {
return nil, nil, fmt.Errorf("unable to open database: %v", err)
}
return db, &baseDb, nil
}

@ -65,18 +65,6 @@ type SwapStore interface {
// it's decoding using the proto package's `Unmarshal` method.
FetchLiquidityParams(ctx context.Context) ([]byte, error)
// BatchUpdateLoopOutSwapCosts updates the swap costs for a batch of
// loop out swaps.
BatchUpdateLoopOutSwapCosts(ctx context.Context,
swaps map[lntypes.Hash]SwapCost) error
// HasMigration returns true if the migration with the given ID has
// been done.
HasMigration(ctx context.Context, migrationID string) (bool, error)
// SetMigration marks the migration with the given ID as done.
SetMigration(ctx context.Context, migrationID string) error
// Close closes the underlying database.
Close() error
}

@ -61,10 +61,6 @@ type LoopOutContract struct {
// allow the server to delay the publication in exchange for possibly
// lower fees.
SwapPublicationDeadline time.Time
// PaymentTimeout is the timeout for any individual off-chain payment
// attempt.
PaymentTimeout time.Duration
}
// ChannelSet stores a set of channels.

@ -32,7 +32,7 @@ const (
ProtocolVersionUserExpiryLoopOut ProtocolVersion = 4
// ProtocolVersionHtlcV2 indicates that the client will use the new
// HTLC v2 scripts for swaps.
// HTLC v2 scrips for swaps.
ProtocolVersionHtlcV2 ProtocolVersion = 5
// ProtocolVersionMultiLoopIn indicates that the client creates a probe

@ -407,61 +407,6 @@ func (s *BaseDB) BatchInsertUpdate(ctx context.Context,
})
}
// BatchUpdateLoopOutSwapCosts updates the swap costs for a batch of loop out
// swaps.
func (b *BaseDB) BatchUpdateLoopOutSwapCosts(ctx context.Context,
costs map[lntypes.Hash]SwapCost) error {
writeOpts := &SqliteTxOptions{}
return b.ExecTx(ctx, writeOpts, func(tx *sqlc.Queries) error {
for swapHash, cost := range costs {
lastUpdateID, err := tx.GetLastUpdateID(
ctx, swapHash[:],
)
if err != nil {
return err
}
err = tx.OverrideSwapCosts(
ctx, sqlc.OverrideSwapCostsParams{
ID: lastUpdateID,
ServerCost: int64(cost.Server),
OnchainCost: int64(cost.Onchain),
OffchainCost: int64(cost.Offchain),
},
)
if err != nil {
return err
}
}
return nil
})
}
// HasMigration returns true if the migration with the given ID has been done.
func (b *BaseDB) HasMigration(ctx context.Context, migrationID string) (
bool, error) {
migration, err := b.GetMigration(ctx, migrationID)
if err != nil && !errors.Is(err, sql.ErrNoRows) {
return false, err
}
return migration.MigrationTs.Valid, nil
}
// SetMigration marks the migration with the given ID as done.
func (b *BaseDB) SetMigration(ctx context.Context, migrationID string) error {
return b.InsertMigration(ctx, sqlc.InsertMigrationParams{
MigrationID: migrationID,
MigrationTs: sql.NullTime{
Time: time.Now().UTC(),
Valid: true,
},
})
}
// loopToInsertArgs converts a SwapContract struct to the arguments needed to
// insert it into the database.
func loopToInsertArgs(hash lntypes.Hash,
@ -498,7 +443,6 @@ func loopOutToInsertArgs(hash lntypes.Hash,
PrepayInvoice: loopOut.PrepayInvoice,
MaxPrepayRoutingFee: int64(loopOut.MaxPrepayRoutingFee),
PublicationDeadline: loopOut.SwapPublicationDeadline.UTC(),
PaymentTimeout: int32(loopOut.PaymentTimeout.Seconds()),
}
}
@ -592,9 +536,6 @@ func ConvertLoopOutRow(network *chaincfg.Params, row sqlc.GetLoopOutSwapRow,
PrepayInvoice: row.PrepayInvoice,
MaxPrepayRoutingFee: btcutil.Amount(row.MaxPrepayRoutingFee),
SwapPublicationDeadline: row.PublicationDeadline,
PaymentTimeout: time.Duration(
row.PaymentTimeout,
) * time.Second,
},
Loop: Loop{
Hash: swapHash,

@ -13,7 +13,6 @@ import (
"github.com/lightninglabs/loop/loopdb/sqlc"
"github.com/lightninglabs/loop/test"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/routing/route"
"github.com/stretchr/testify/require"
)
@ -61,7 +60,6 @@ func TestSqliteLoopOutStore(t *testing.T) {
SweepConfTarget: 2,
HtlcConfirmations: 2,
SwapPublicationDeadline: initiationTime,
PaymentTimeout: time.Second * 11,
}
t.Run("no outgoing set", func(t *testing.T) {
@ -122,8 +120,6 @@ func testSqliteLoopOutStore(t *testing.T, pendingSwap *LoopOutContract) {
if expectedState == StatePreimageRevealed {
require.NotNil(t, swap.State().HtlcTxHash)
}
require.Equal(t, time.Second*11, swap.Contract.PaymentTimeout)
}
// If we create a new swap, then it should show up as being initialized
@ -397,140 +393,6 @@ func TestIssue615(t *testing.T) {
require.NoError(t, err)
}
// TestBatchUpdateCost tests that we can batch update the cost of multiple swaps
// at once.
func TestBatchUpdateCost(t *testing.T) {
// Create a new sqlite store for testing.
store := NewTestDB(t)
destAddr := test.GetDestAddr(t, 0)
initiationTime := time.Date(2018, 11, 1, 0, 0, 0, 0, time.UTC)
testContract := LoopOutContract{
SwapContract: SwapContract{
AmountRequested: 100,
CltvExpiry: 144,
HtlcKeys: HtlcKeys{
SenderScriptKey: senderKey,
ReceiverScriptKey: receiverKey,
SenderInternalPubKey: senderInternalKey,
ReceiverInternalPubKey: receiverInternalKey,
ClientScriptKeyLocator: keychain.KeyLocator{
Family: 1,
Index: 2,
},
},
MaxMinerFee: 10,
MaxSwapFee: 20,
InitiationHeight: 99,
InitiationTime: initiationTime,
ProtocolVersion: ProtocolVersionMuSig2,
},
MaxPrepayRoutingFee: 40,
PrepayInvoice: "prepayinvoice",
DestAddr: destAddr,
SwapInvoice: "swapinvoice",
MaxSwapRoutingFee: 30,
SweepConfTarget: 2,
HtlcConfirmations: 2,
SwapPublicationDeadline: initiationTime,
PaymentTimeout: time.Second * 11,
}
makeSwap := func(preimage lntypes.Preimage) *LoopOutContract {
contract := testContract
contract.Preimage = preimage
return &contract
}
// Next, we'll add two swaps to the database.
preimage1 := testPreimage
preimage2 := lntypes.Preimage{4, 4, 4}
ctxb := context.Background()
swap1 := makeSwap(preimage1)
swap2 := makeSwap(preimage2)
hash1 := swap1.Preimage.Hash()
err := store.CreateLoopOut(ctxb, hash1, swap1)
require.NoError(t, err)
hash2 := swap2.Preimage.Hash()
err = store.CreateLoopOut(ctxb, hash2, swap2)
require.NoError(t, err)
// Add an update to both swaps containing the cost.
err = store.UpdateLoopOut(
ctxb, hash1, testTime,
SwapStateData{
State: StateSuccess,
Cost: SwapCost{
Server: 1,
Onchain: 2,
Offchain: 3,
},
},
)
require.NoError(t, err)
err = store.UpdateLoopOut(
ctxb, hash2, testTime,
SwapStateData{
State: StateSuccess,
Cost: SwapCost{
Server: 4,
Onchain: 5,
Offchain: 6,
},
},
)
require.NoError(t, err)
updateMap := map[lntypes.Hash]SwapCost{
hash1: {
Server: 2,
Onchain: 3,
Offchain: 4,
},
hash2: {
Server: 6,
Onchain: 7,
Offchain: 8,
},
}
require.NoError(t, store.BatchUpdateLoopOutSwapCosts(ctxb, updateMap))
swaps, err := store.FetchLoopOutSwaps(ctxb)
require.NoError(t, err)
require.Len(t, swaps, 2)
swapsMap := make(map[lntypes.Hash]*LoopOut)
swapsMap[swaps[0].Hash] = swaps[0]
swapsMap[swaps[1].Hash] = swaps[1]
require.Equal(t, updateMap[hash1], swapsMap[hash1].State().Cost)
require.Equal(t, updateMap[hash2], swapsMap[hash2].State().Cost)
}
// TestMigrationTracker tests the migration tracker functionality.
func TestMigrationTracker(t *testing.T) {
ctxb := context.Background()
// Create a new sqlite store for testing.
sqlDB := NewTestDB(t)
hasMigration, err := sqlDB.HasMigration(ctxb, "test")
require.NoError(t, err)
require.False(t, hasMigration)
require.NoError(t, sqlDB.SetMigration(ctxb, "test"))
hasMigration, err = sqlDB.HasMigration(ctxb, "test")
require.NoError(t, err)
require.True(t, hasMigration)
}
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
func randomString(length int) string {

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.25.0
// sqlc v1.17.2
// source: batch.sql
package sqlc
@ -8,6 +8,7 @@ package sqlc
import (
"context"
"database/sql"
"time"
)
const confirmBatch = `-- name: ConfirmBatch :exec
@ -24,35 +25,74 @@ func (q *Queries) ConfirmBatch(ctx context.Context, id int32) error {
return err
}
const dropBatch = `-- name: DropBatch :exec
DELETE FROM sweep_batches WHERE id = $1
`
func (q *Queries) DropBatch(ctx context.Context, id int32) error {
_, err := q.db.ExecContext(ctx, dropBatch, id)
return err
}
const getBatchSweeps = `-- name: GetBatchSweeps :many
SELECT
id, swap_hash, batch_id, outpoint_txid, outpoint_index, amt, completed
sweeps.id, sweeps.swap_hash, sweeps.batch_id, sweeps.outpoint_txid, sweeps.outpoint_index, sweeps.amt, sweeps.completed,
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,
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
FROM
sweeps
JOIN
swaps ON sweeps.swap_hash = swaps.swap_hash
JOIN
loopout_swaps ON sweeps.swap_hash = loopout_swaps.swap_hash
JOIN
htlc_keys ON sweeps.swap_hash = htlc_keys.swap_hash
WHERE
batch_id = $1
sweeps.batch_id = $1
ORDER BY
id ASC
sweeps.id ASC
`
func (q *Queries) GetBatchSweeps(ctx context.Context, batchID int32) ([]Sweep, error) {
type GetBatchSweepsRow struct {
ID int32
SwapHash []byte
BatchID int32
OutpointTxid []byte
OutpointIndex int32
Amt int64
Completed bool
ID_2 int32
SwapHash_2 []byte
Preimage []byte
InitiationTime time.Time
AmountRequested int64
CltvExpiry int32
MaxMinerFee int64
MaxSwapFee int64
InitiationHeight int32
ProtocolVersion int32
Label string
SwapHash_3 []byte
DestAddress string
SwapInvoice string
MaxSwapRoutingFee int64
SweepConfTarget int32
HtlcConfirmations int32
OutgoingChanSet string
PrepayInvoice string
MaxPrepayRoutingFee int64
PublicationDeadline time.Time
SingleSweep bool
SwapHash_4 []byte
SenderScriptPubkey []byte
ReceiverScriptPubkey []byte
SenderInternalPubkey []byte
ReceiverInternalPubkey []byte
ClientKeyFamily int32
ClientKeyIndex int32
}
func (q *Queries) GetBatchSweeps(ctx context.Context, batchID int32) ([]GetBatchSweepsRow, error) {
rows, err := q.db.QueryContext(ctx, getBatchSweeps, batchID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Sweep
var items []GetBatchSweepsRow
for rows.Next() {
var i Sweep
var i GetBatchSweepsRow
if err := rows.Scan(
&i.ID,
&i.SwapHash,
@ -61,6 +101,35 @@ func (q *Queries) GetBatchSweeps(ctx context.Context, batchID int32) ([]Sweep, e
&i.OutpointIndex,
&i.Amt,
&i.Completed,
&i.ID_2,
&i.SwapHash_2,
&i.Preimage,
&i.InitiationTime,
&i.AmountRequested,
&i.CltvExpiry,
&i.MaxMinerFee,
&i.MaxSwapFee,
&i.InitiationHeight,
&i.ProtocolVersion,
&i.Label,
&i.SwapHash_3,
&i.DestAddress,
&i.SwapInvoice,
&i.MaxSwapRoutingFee,
&i.SweepConfTarget,
&i.HtlcConfirmations,
&i.OutgoingChanSet,
&i.PrepayInvoice,
&i.MaxPrepayRoutingFee,
&i.PublicationDeadline,
&i.SingleSweep,
&i.SwapHash_4,
&i.SenderScriptPubkey,
&i.ReceiverScriptPubkey,
&i.SenderInternalPubkey,
&i.ReceiverInternalPubkey,
&i.ClientKeyFamily,
&i.ClientKeyIndex,
); err != nil {
return nil, err
}
@ -75,54 +144,6 @@ func (q *Queries) GetBatchSweeps(ctx context.Context, batchID int32) ([]Sweep, e
return items, nil
}
const getBatchSweptAmount = `-- name: GetBatchSweptAmount :one
SELECT
SUM(amt) AS total
FROM
sweeps
WHERE
batch_id = $1
AND
completed = TRUE
`
func (q *Queries) GetBatchSweptAmount(ctx context.Context, batchID int32) (int64, error) {
row := q.db.QueryRowContext(ctx, getBatchSweptAmount, batchID)
var total int64
err := row.Scan(&total)
return total, err
}
const getParentBatch = `-- name: GetParentBatch :one
SELECT
sweep_batches.id, sweep_batches.confirmed, sweep_batches.batch_tx_id, sweep_batches.batch_pk_script, sweep_batches.last_rbf_height, sweep_batches.last_rbf_sat_per_kw, sweep_batches.max_timeout_distance
FROM
sweep_batches
JOIN
sweeps ON sweep_batches.id = sweeps.batch_id
WHERE
sweeps.swap_hash = $1
AND
sweeps.completed = TRUE
AND
sweep_batches.confirmed = TRUE
`
func (q *Queries) GetParentBatch(ctx context.Context, swapHash []byte) (SweepBatch, error) {
row := q.db.QueryRowContext(ctx, getParentBatch, swapHash)
var i SweepBatch
err := row.Scan(
&i.ID,
&i.Confirmed,
&i.BatchTxID,
&i.BatchPkScript,
&i.LastRbfHeight,
&i.LastRbfSatPerKw,
&i.MaxTimeoutDistance,
)
return i, err
}
const getSweepStatus = `-- name: GetSweepStatus :one
SELECT
COALESCE(s.completed, f.false_value) AS completed

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.25.0
// sqlc v1.17.2
package sqlc

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.25.0
// sqlc v1.17.2
// source: instantout.sql
package sqlc
@ -133,7 +133,7 @@ func (q *Queries) GetInstantOutSwapUpdates(ctx context.Context, swapHash []byte)
}
const getInstantOutSwaps = `-- name: GetInstantOutSwaps :many
SELECT
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,
instantout_swaps.swap_hash, instantout_swaps.preimage, instantout_swaps.sweep_address, instantout_swaps.outgoing_chan_set, instantout_swaps.htlc_fee_rate, instantout_swaps.reservation_ids, instantout_swaps.swap_invoice, instantout_swaps.finalized_htlc_tx, instantout_swaps.sweep_tx_hash, instantout_swaps.finalized_sweepless_sweep_tx, instantout_swaps.sweep_confirmation_height,
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
@ -235,7 +235,7 @@ func (q *Queries) GetInstantOutSwaps(ctx context.Context) ([]GetInstantOutSwapsR
const insertInstantOut = `-- name: InsertInstantOut :exec
INSERT INTO instantout_swaps (
swap_hash,
preimage,
preimage,
sweep_address,
outgoing_chan_set,
htlc_fee_rate,

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.25.0
// sqlc v1.17.2
// source: liquidity_params.sql
package sqlc

@ -1,45 +0,0 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.25.0
// source: migration_tracker.sql
package sqlc
import (
"context"
"database/sql"
)
const getMigration = `-- name: GetMigration :one
SELECT
migration_id,
migration_ts
FROM
migration_tracker
WHERE
migration_id = $1
`
func (q *Queries) GetMigration(ctx context.Context, migrationID string) (MigrationTracker, error) {
row := q.db.QueryRowContext(ctx, getMigration, migrationID)
var i MigrationTracker
err := row.Scan(&i.MigrationID, &i.MigrationTs)
return i, err
}
const insertMigration = `-- name: InsertMigration :exec
INSERT INTO migration_tracker (
migration_id,
migration_ts
) VALUES ($1, $2)
`
type InsertMigrationParams struct {
MigrationID string
MigrationTs sql.NullTime
}
func (q *Queries) InsertMigration(ctx context.Context, arg InsertMigrationParams) error {
_, err := q.db.ExecContext(ctx, insertMigration, arg.MigrationID, arg.MigrationTs)
return err
}

@ -1,3 +0,0 @@
-- payment_timeout is the timeout in seconds for each individual off-chain
-- payment.
ALTER TABLE loopout_swaps DROP COLUMN payment_timeout;

@ -1,3 +0,0 @@
-- payment_timeout is the timeout in seconds for each individual off-chain
-- payment.
ALTER TABLE loopout_swaps ADD payment_timeout INTEGER NOT NULL DEFAULT 0;

@ -1,9 +0,0 @@
CREATE TABLE migration_tracker (
-- migration_id is the id of the migration.
migration_id TEXT NOT NULL,
-- migration_ts is the timestamp at which the migration was run.
migration_ts TIMESTAMP,
PRIMARY KEY (migration_id)
);

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.25.0
// sqlc v1.17.2
package sqlc
@ -64,12 +64,6 @@ type LoopoutSwap struct {
MaxPrepayRoutingFee int64
PublicationDeadline time.Time
SingleSweep bool
PaymentTimeout int32
}
type MigrationTracker struct {
MigrationID string
MigrationTs sql.NullTime
}
type Reservation struct {

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.25.0
// sqlc v1.17.2
package sqlc
@ -11,20 +11,15 @@ import (
type Querier interface {
ConfirmBatch(ctx context.Context, id int32) error
CreateReservation(ctx context.Context, arg CreateReservationParams) error
DropBatch(ctx context.Context, id int32) error
FetchLiquidityParams(ctx context.Context) ([]byte, error)
GetBatchSweeps(ctx context.Context, batchID int32) ([]Sweep, error)
GetBatchSweptAmount(ctx context.Context, batchID int32) (int64, error)
GetBatchSweeps(ctx context.Context, batchID int32) ([]GetBatchSweepsRow, error)
GetInstantOutSwap(ctx context.Context, swapHash []byte) (GetInstantOutSwapRow, error)
GetInstantOutSwapUpdates(ctx context.Context, swapHash []byte) ([]InstantoutUpdate, error)
GetInstantOutSwaps(ctx context.Context) ([]GetInstantOutSwapsRow, error)
GetLastUpdateID(ctx context.Context, swapHash []byte) (int32, error)
GetLoopInSwap(ctx context.Context, swapHash []byte) (GetLoopInSwapRow, error)
GetLoopInSwaps(ctx context.Context) ([]GetLoopInSwapsRow, error)
GetLoopOutSwap(ctx context.Context, swapHash []byte) (GetLoopOutSwapRow, error)
GetLoopOutSwaps(ctx context.Context) ([]GetLoopOutSwapsRow, error)
GetMigration(ctx context.Context, migrationID string) (MigrationTracker, error)
GetParentBatch(ctx context.Context, swapHash []byte) (SweepBatch, error)
GetReservation(ctx context.Context, reservationID []byte) (Reservation, error)
GetReservationUpdates(ctx context.Context, reservationID []byte) ([]ReservationUpdate, error)
GetReservations(ctx context.Context) ([]Reservation, error)
@ -37,11 +32,9 @@ type Querier interface {
InsertInstantOutUpdate(ctx context.Context, arg InsertInstantOutUpdateParams) error
InsertLoopIn(ctx context.Context, arg InsertLoopInParams) error
InsertLoopOut(ctx context.Context, arg InsertLoopOutParams) error
InsertMigration(ctx context.Context, arg InsertMigrationParams) error
InsertReservationUpdate(ctx context.Context, arg InsertReservationUpdateParams) error
InsertSwap(ctx context.Context, arg InsertSwapParams) error
InsertSwapUpdate(ctx context.Context, arg InsertSwapUpdateParams) error
OverrideSwapCosts(ctx context.Context, arg OverrideSwapCostsParams) error
UpdateBatch(ctx context.Context, arg UpdateBatchParams) error
UpdateInstantOut(ctx context.Context, arg UpdateInstantOutParams) error
UpdateReservation(ctx context.Context, arg UpdateReservationParams) error

@ -23,9 +23,6 @@ INSERT INTO sweep_batches (
$6
) RETURNING id;
-- name: DropBatch :exec
DELETE FROM sweep_batches WHERE id = $1;
-- name: UpdateBatch :exec
UPDATE sweep_batches SET
confirmed = $2,
@ -65,39 +62,25 @@ INSERT INTO sweeps (
amt = $5,
completed = $6;
-- name: GetParentBatch :one
SELECT
sweep_batches.*
FROM
sweep_batches
JOIN
sweeps ON sweep_batches.id = sweeps.batch_id
WHERE
sweeps.swap_hash = $1
AND
sweeps.completed = TRUE
AND
sweep_batches.confirmed = TRUE;
-- name: GetBatchSweptAmount :one
SELECT
SUM(amt) AS total
FROM
sweeps
WHERE
batch_id = $1
AND
completed = TRUE;
-- name: GetBatchSweeps :many
SELECT
*
sweeps.*,
swaps.*,
loopout_swaps.*,
htlc_keys.*
FROM
sweeps
JOIN
swaps ON sweeps.swap_hash = swaps.swap_hash
JOIN
loopout_swaps ON sweeps.swap_hash = loopout_swaps.swap_hash
JOIN
htlc_keys ON sweeps.swap_hash = htlc_keys.swap_hash
WHERE
batch_id = $1
sweeps.batch_id = $1
ORDER BY
id ASC;
sweeps.id ASC;
-- name: GetSweepStatus :one
SELECT

@ -1,7 +1,7 @@
-- name: InsertInstantOut :exec
INSERT INTO instantout_swaps (
swap_hash,
preimage,
preimage,
sweep_address,
outgoing_chan_set,
htlc_fee_rate,
@ -53,7 +53,7 @@ WHERE
swaps.swap_hash = $1;
-- name: GetInstantOutSwaps :many
SELECT
SELECT
swaps.*,
instantout_swaps.*,
htlc_keys.*

@ -1,14 +0,0 @@
-- name: InsertMigration :exec
INSERT INTO migration_tracker (
migration_id,
migration_ts
) VALUES ($1, $2);
-- name: GetMigration :one
SELECT
migration_id,
migration_ts
FROM
migration_tracker
WHERE
migration_id = $1;

@ -1,9 +1,9 @@
-- name: GetLoopOutSwaps :many
SELECT
SELECT
swaps.*,
loopout_swaps.*,
htlc_keys.*
FROM
FROM
swaps
JOIN
loopout_swaps ON swaps.swap_hash = loopout_swaps.swap_hash
@ -13,7 +13,7 @@ ORDER BY
swaps.id;
-- name: GetLoopOutSwap :one
SELECT
SELECT
swaps.*,
loopout_swaps.*,
htlc_keys.*
@ -27,7 +27,7 @@ WHERE
swaps.swap_hash = $1;
-- name: GetLoopInSwaps :many
SELECT
SELECT
swaps.*,
loopin_swaps.*,
htlc_keys.*
@ -41,7 +41,7 @@ ORDER BY
swaps.id;
-- name: GetLoopInSwap :one
SELECT
SELECT
swaps.*,
loopin_swaps.*,
htlc_keys.*
@ -55,7 +55,7 @@ WHERE
swaps.swap_hash = $1;
-- name: GetSwapUpdates :many
SELECT
SELECT
*
FROM
swap_updates
@ -105,10 +105,9 @@ INSERT INTO loopout_swaps (
prepay_invoice,
max_prepay_routing_fee,
publication_deadline,
single_sweep,
payment_timeout
single_sweep
) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11
);
-- name: InsertLoopIn :exec
@ -132,20 +131,4 @@ INSERT INTO htlc_keys(
client_key_index
) VALUES (
$1, $2, $3, $4, $5, $6, $7
);
-- name: GetLastUpdateID :one
SELECT id
FROM swap_updates
WHERE swap_hash = $1
ORDER BY update_timestamp DESC
LIMIT 1;
-- name: OverrideSwapCosts :exec
UPDATE swap_updates
SET
server_cost = $2,
onchain_cost = $3,
offchain_cost = $4
WHERE id = $1;
);

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.25.0
// sqlc v1.17.2
// source: reservations.sql
package sqlc

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.25.0
// sqlc v1.17.2
// source: swaps.sql
package sqlc
@ -10,23 +10,8 @@ import (
"time"
)
const getLastUpdateID = `-- name: GetLastUpdateID :one
SELECT id
FROM swap_updates
WHERE swap_hash = $1
ORDER BY update_timestamp DESC
LIMIT 1
`
func (q *Queries) GetLastUpdateID(ctx context.Context, swapHash []byte) (int32, error) {
row := q.db.QueryRowContext(ctx, getLastUpdateID, swapHash)
var id int32
err := row.Scan(&id)
return id, err
}
const getLoopInSwap = `-- name: GetLoopInSwap :one
SELECT
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,
loopin_swaps.swap_hash, loopin_swaps.htlc_conf_target, loopin_swaps.last_hop, loopin_swaps.external_htlc,
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
@ -96,7 +81,7 @@ func (q *Queries) GetLoopInSwap(ctx context.Context, swapHash []byte) (GetLoopIn
}
const getLoopInSwaps = `-- name: GetLoopInSwaps :many
SELECT
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,
loopin_swaps.swap_hash, loopin_swaps.htlc_conf_target, loopin_swaps.last_hop, loopin_swaps.external_htlc,
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
@ -182,9 +167,9 @@ func (q *Queries) GetLoopInSwaps(ctx context.Context) ([]GetLoopInSwapsRow, erro
}
const getLoopOutSwap = `-- name: GetLoopOutSwap :one
SELECT
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,
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,
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
FROM
swaps
@ -219,7 +204,6 @@ type GetLoopOutSwapRow struct {
MaxPrepayRoutingFee int64
PublicationDeadline time.Time
SingleSweep bool
PaymentTimeout int32
SwapHash_3 []byte
SenderScriptPubkey []byte
ReceiverScriptPubkey []byte
@ -255,7 +239,6 @@ func (q *Queries) GetLoopOutSwap(ctx context.Context, swapHash []byte) (GetLoopO
&i.MaxPrepayRoutingFee,
&i.PublicationDeadline,
&i.SingleSweep,
&i.PaymentTimeout,
&i.SwapHash_3,
&i.SenderScriptPubkey,
&i.ReceiverScriptPubkey,
@ -268,11 +251,11 @@ func (q *Queries) GetLoopOutSwap(ctx context.Context, swapHash []byte) (GetLoopO
}
const getLoopOutSwaps = `-- name: GetLoopOutSwaps :many
SELECT
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,
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,
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
FROM
FROM
swaps
JOIN
loopout_swaps ON swaps.swap_hash = loopout_swaps.swap_hash
@ -305,7 +288,6 @@ type GetLoopOutSwapsRow struct {
MaxPrepayRoutingFee int64
PublicationDeadline time.Time
SingleSweep bool
PaymentTimeout int32
SwapHash_3 []byte
SenderScriptPubkey []byte
ReceiverScriptPubkey []byte
@ -347,7 +329,6 @@ func (q *Queries) GetLoopOutSwaps(ctx context.Context) ([]GetLoopOutSwapsRow, er
&i.MaxPrepayRoutingFee,
&i.PublicationDeadline,
&i.SingleSweep,
&i.PaymentTimeout,
&i.SwapHash_3,
&i.SenderScriptPubkey,
&i.ReceiverScriptPubkey,
@ -370,7 +351,7 @@ func (q *Queries) GetLoopOutSwaps(ctx context.Context) ([]GetLoopOutSwapsRow, er
}
const getSwapUpdates = `-- name: GetSwapUpdates :many
SELECT
SELECT
id, swap_hash, update_timestamp, update_state, htlc_txhash, server_cost, onchain_cost, offchain_cost
FROM
swap_updates
@ -489,10 +470,9 @@ INSERT INTO loopout_swaps (
prepay_invoice,
max_prepay_routing_fee,
publication_deadline,
single_sweep,
payment_timeout
single_sweep
) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11
)
`
@ -508,7 +488,6 @@ type InsertLoopOutParams struct {
MaxPrepayRoutingFee int64
PublicationDeadline time.Time
SingleSweep bool
PaymentTimeout int32
}
func (q *Queries) InsertLoopOut(ctx context.Context, arg InsertLoopOutParams) error {
@ -524,7 +503,6 @@ func (q *Queries) InsertLoopOut(ctx context.Context, arg InsertLoopOutParams) er
arg.MaxPrepayRoutingFee,
arg.PublicationDeadline,
arg.SingleSweep,
arg.PaymentTimeout,
)
return err
}
@ -611,29 +589,3 @@ func (q *Queries) InsertSwapUpdate(ctx context.Context, arg InsertSwapUpdatePara
)
return err
}
const overrideSwapCosts = `-- name: OverrideSwapCosts :exec
UPDATE swap_updates
SET
server_cost = $2,
onchain_cost = $3,
offchain_cost = $4
WHERE id = $1
`
type OverrideSwapCostsParams struct {
ID int32
ServerCost int64
OnchainCost int64
OffchainCost int64
}
func (q *Queries) OverrideSwapCosts(ctx context.Context, arg OverrideSwapCostsParams) error {
_, err := q.db.ExecContext(ctx, overrideSwapCosts,
arg.ID,
arg.ServerCost,
arg.OnchainCost,
arg.OffchainCost,
)
return err
}

@ -15,6 +15,7 @@ import (
sqlite_migrate "github.com/golang-migrate/migrate/v4/database/sqlite"
"github.com/lightninglabs/loop/loopdb/sqlc"
"github.com/lightningnetwork/lnd/zpay32"
"github.com/stretchr/testify/require"
_ "modernc.org/sqlite" // Register relevant drivers.
)
@ -67,21 +68,6 @@ func NewSqliteStore(cfg *SqliteConfig, network *chaincfg.Params) (*SqliteSwapSto
name: "busy_timeout",
value: "5000",
},
{
// With the WAL mode, this ensures that we also do an
// extra WAL sync after each transaction. The normal
// sync mode skips this and gives better performance,
// but risks durability.
name: "synchronous",
value: "full",
},
{
// This is used to ensure proper durability for users
// running on Mac OS. It uses the correct fsync system
// call to ensure items are fully flushed to disk.
name: "fullfsync",
value: "true",
},
}
sqliteOptions := make(url.Values)
for _, option := range pragmaOptions {

@ -1009,25 +1009,3 @@ func (b *boltSwapStore) BatchInsertUpdate(ctx context.Context,
return errUnimplemented
}
// BatchUpdateLoopOutSwapCosts updates the swap costs for a batch of loop out
// swaps.
func (b *boltSwapStore) BatchUpdateLoopOutSwapCosts(ctx context.Context,
costs map[lntypes.Hash]SwapCost) error {
return errUnimplemented
}
// HasMigration returns true if the migration with the given ID has been done.
func (b *boltSwapStore) HasMigration(ctx context.Context, migrationID string) (
bool, error) {
return false, errUnimplemented
}
// SetMigration marks the migration with the given ID as done.
func (b *boltSwapStore) SetMigration(ctx context.Context,
migrationID string) error {
return errUnimplemented
}

@ -3,7 +3,6 @@ package loopdb
import (
"context"
"errors"
"fmt"
"testing"
"time"
@ -24,8 +23,6 @@ type StoreMock struct {
loopInStoreChan chan LoopInContract
loopInUpdateChan chan SwapStateData
migrations map[string]struct{}
t *testing.T
}
@ -41,7 +38,6 @@ func NewStoreMock(t *testing.T) *StoreMock {
loopInUpdateChan: make(chan SwapStateData, 1),
LoopInSwaps: make(map[lntypes.Hash]*LoopInContract),
LoopInUpdates: make(map[lntypes.Hash][]SwapStateData),
migrations: make(map[string]struct{}),
t: t,
}
}
@ -341,46 +337,3 @@ func (b *StoreMock) BatchInsertUpdate(ctx context.Context,
return errors.New("not implemented")
}
// BatchUpdateLoopOutSwapCosts updates the swap costs for a batch of loop out
// swaps.
func (s *StoreMock) BatchUpdateLoopOutSwapCosts(ctx context.Context,
costs map[lntypes.Hash]SwapCost) error {
for hash, cost := range costs {
if _, ok := s.LoopOutUpdates[hash]; !ok {
return fmt.Errorf("swap has no updates: %v", hash)
}
updates, ok := s.LoopOutUpdates[hash]
if !ok {
return fmt.Errorf("swap has no updates: %v", hash)
}
updates[len(updates)-1].Cost = cost
}
return nil
}
// HasMigration returns true if the migration with the given ID has been done.
func (s *StoreMock) HasMigration(ctx context.Context, migrationID string) (
bool, error) {
_, ok := s.migrations[migrationID]
return ok, nil
}
// SetMigration marks the migration with the given ID as done.
func (s *StoreMock) SetMigration(ctx context.Context,
migrationID string) error {
if _, ok := s.migrations[migrationID]; ok {
return errors.New("migration already done")
}
s.migrations[migrationID] = struct{}{}
return nil
}

@ -111,8 +111,8 @@ func newLoopInSwap(globalCtx context.Context, cfg *swapConfig,
// Because the Private flag is set, we'll generate our own set
// of hop hints.
request.RouteHints, err = SelectHopHints(
globalCtx, cfg.lnd.Client, request.Amount,
DefaultMaxHopHints, includeNodes,
globalCtx, cfg.lnd, request.Amount, DefaultMaxHopHints,
includeNodes,
)
if err != nil {
return nil, err
@ -837,7 +837,7 @@ func getTxFee(tx *wire.MsgTx, fee chainfee.SatPerKVByte) btcutil.Amount {
btcTx := btcutil.NewTx(tx)
vsize := mempool.GetTxVirtualSize(btcTx)
return fee.FeeForVSize(lntypes.VByte(vsize))
return fee.FeeForVSize(vsize)
}
// waitForSwapComplete waits until a spending tx of the htlc gets confirmed and
@ -919,7 +919,9 @@ func (s *loopInSwap) waitForSwapComplete(ctx context.Context,
s.log.Infof("Htlc spend by tx: %v",
spendDetails.SpenderTxHash)
err := s.processHtlcSpend(ctx, spendDetails, sweepFee)
err := s.processHtlcSpend(
ctx, spendDetails, htlcValue, sweepFee,
)
if err != nil {
return err
}
@ -957,6 +959,8 @@ func (s *loopInSwap) waitForSwapComplete(ctx context.Context,
switch update.State {
// Swap invoice was paid, so update server cost balance.
case invpkg.ContractSettled:
s.cost.Server -= update.AmtPaid
// If invoice settlement and htlc spend happen
// in the expected order, move the swap to an
// intermediate state that indicates that the
@ -973,8 +977,6 @@ func (s *loopInSwap) waitForSwapComplete(ctx context.Context,
invoiceFinalized = true
htlcKeyRevealed = s.tryPushHtlcKey(ctx)
s.cost.Server = s.AmountRequested -
update.AmtPaid
// Canceled invoice has no effect on server cost
// balance.
@ -1021,7 +1023,8 @@ func (s *loopInSwap) tryPushHtlcKey(ctx context.Context) bool {
}
func (s *loopInSwap) processHtlcSpend(ctx context.Context,
spend *chainntnfs.SpendDetail, sweepFee btcutil.Amount) error {
spend *chainntnfs.SpendDetail, htlcValue,
sweepFee btcutil.Amount) error {
// Determine the htlc input of the spending tx and inspect the witness
// to find out whether a success or a timeout tx spent the htlc.
@ -1029,6 +1032,10 @@ func (s *loopInSwap) processHtlcSpend(ctx context.Context,
if s.htlc.IsSuccessWitness(htlcInput.Witness) {
s.setState(loopdb.StateSuccess)
// Server swept the htlc. The htlc value can be added to the
// server cost balance.
s.cost.Server += htlcValue
} else {
// We needed another on chain tx to sweep the timeout clause,
// which we now include in our costs.

@ -77,10 +77,6 @@ type loopOutSwap struct {
swapInvoicePaymentAddr [32]byte
// prepayAmount holds the amount of the prepay invoice. We use this
// to calculate the total cost of the swap.
prepayAmount btcutil.Amount
swapPaymentChan chan paymentResult
prePaymentChan chan paymentResult
@ -199,7 +195,6 @@ func newLoopOutSwap(globalCtx context.Context, cfg *swapConfig,
ProtocolVersion: loopdb.CurrentProtocolVersion(),
},
OutgoingChanSet: chanSet,
PaymentTimeout: request.PaymentTimeout,
}
swapKit := newSwapKit(
@ -311,11 +306,7 @@ func (s *loopOutSwap) sendUpdate(ctx context.Context) error {
info := s.swapInfo()
s.log.Infof("Loop out swap state: %v", info.State)
if s.htlc.OutputType == swap.HtlcP2WSH {
info.HtlcAddressP2WSH = s.htlc.Address
} else {
info.HtlcAddressP2TR = s.htlc.Address
}
info.HtlcAddressP2WSH = s.htlc.Address
// In order to avoid potentially dangerous ownership sharing
// we copy the outgoing channel set.
@ -407,7 +398,7 @@ func (s *loopOutSwap) executeAndFinalize(globalCtx context.Context) error {
case result := <-s.swapPaymentChan:
s.swapPaymentChan = nil
err := s.handlePaymentResult(result, true)
err := s.handlePaymentResult(result)
if err != nil {
return err
}
@ -423,7 +414,7 @@ func (s *loopOutSwap) executeAndFinalize(globalCtx context.Context) error {
case result := <-s.prePaymentChan:
s.prePaymentChan = nil
err := s.handlePaymentResult(result, false)
err := s.handlePaymentResult(result)
if err != nil {
return err
}
@ -453,12 +444,7 @@ func (s *loopOutSwap) executeAndFinalize(globalCtx context.Context) error {
return s.persistState(globalCtx)
}
// 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(result paymentResult) error {
switch {
// If our result has a non-nil error, our status will be nil. In this
// case the payment failed so we do not need to take any action.
@ -466,22 +452,7 @@ func (s *loopOutSwap) handlePaymentResult(result paymentResult,
return nil
case result.status.State == lnrpc.Payment_SUCCEEDED:
// Update the cost of the swap if this is the main swap payment.
if swapPayment {
// The client pays for the swap with the swap invoice,
// so we can calculate the total cost of the swap by
// subtracting the amount requested from the total
// amount that we actually paid (which is the sum of
// the swap invoice amount and the prepay invoice
// amount).
s.cost.Server += s.prepayAmount +
result.status.Value.ToSatoshis() -
s.AmountRequested
}
// On top of the swap cost we also pay for routing which
// is reflected in the fee. We add the off-chain fee for both
// the swap payment and the prepay.
s.cost.Server += result.status.Value.ToSatoshis()
s.cost.Offchain += result.status.Fee.ToSatoshis()
return nil
@ -497,16 +468,6 @@ func (s *loopOutSwap) handlePaymentResult(result paymentResult,
// executeSwap executes the swap, but returns as soon as the swap outcome is
// final. At that point, there may still be pending off-chain payment(s).
func (s *loopOutSwap) executeSwap(globalCtx context.Context) error {
// Decode the prepay invoice so we can ensure that we account for the
// prepay amount when calculating the final costs of the swap.
_, _, _, amt, err := swap.DecodeInvoice(
s.lnd.ChainParams, s.PrepayInvoice,
)
if err != nil {
return err
}
s.prepayAmount = amt
// We always pay both invoices (again). This is currently the only way
// to sort of resume payments.
//
@ -553,7 +514,7 @@ func (s *loopOutSwap) executeSwap(globalCtx context.Context) error {
}
// Try to spend htlc and continue (rbf) until a spend has confirmed.
spend, err := s.waitForHtlcSpendConfirmedV2(
spendTx, err := s.waitForHtlcSpendConfirmedV2(
globalCtx, *htlcOutpoint, htlcValue,
)
if err != nil {
@ -562,7 +523,7 @@ func (s *loopOutSwap) executeSwap(globalCtx context.Context) error {
// If spend details are nil, we resolved the swap without waiting for
// its spend, so we can exit.
if spend == nil {
if spendTx == nil {
return nil
}
@ -570,7 +531,7 @@ func (s *loopOutSwap) executeSwap(globalCtx context.Context) error {
// don't just try to match with the hash of our sweep tx, because it
// may be swept by a different (fee) sweep tx from a previous run.
htlcInput, err := swap.GetTxInputByOutpoint(
spend.Tx, htlcOutpoint,
spendTx, htlcOutpoint,
)
if err != nil {
return err
@ -578,7 +539,11 @@ func (s *loopOutSwap) executeSwap(globalCtx context.Context) error {
sweepSuccessful := s.htlc.IsSuccessWitness(htlcInput.Witness)
if sweepSuccessful {
s.cost.Onchain = spend.OnChainFeePortion
s.cost.Server -= htlcValue
s.cost.Onchain = htlcValue -
btcutil.Amount(spendTx.TxOut[0].Value)
s.state = loopdb.StateSuccess
} else {
s.state = loopdb.StateFailSweepTimeout
@ -629,18 +594,15 @@ func (s *loopOutSwap) payInvoices(ctx context.Context) {
// Use the recommended routing plugin.
s.swapPaymentChan = s.payInvoice(
ctx, s.SwapInvoice, s.MaxSwapRoutingFee,
s.LoopOutContract.OutgoingChanSet,
s.LoopOutContract.PaymentTimeout, pluginType, true,
s.LoopOutContract.OutgoingChanSet, pluginType, true,
)
// 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.
// prepay is trivially small and shouldn't normally need any help.
s.log.Infof("Sending prepayment %v", s.PrepayInvoice)
s.prePaymentChan = s.payInvoice(
ctx, s.PrepayInvoice, s.MaxPrepayRoutingFee,
s.LoopOutContract.OutgoingChanSet,
s.LoopOutContract.PaymentTimeout, RoutingPluginNone, false,
nil, RoutingPluginNone, false,
)
}
@ -668,7 +630,7 @@ func (p paymentResult) failure() error {
// payInvoice pays a single invoice.
func (s *loopOutSwap) payInvoice(ctx context.Context, invoice string,
maxFee btcutil.Amount, outgoingChanIds loopdb.ChannelSet,
paymentTimeout time.Duration, pluginType RoutingPluginType,
pluginType RoutingPluginType,
reportPluginResult bool) chan paymentResult {
resultChan := make(chan paymentResult)
@ -683,8 +645,8 @@ func (s *loopOutSwap) payInvoice(ctx context.Context, invoice string,
var result paymentResult
status, err := s.payInvoiceAsync(
ctx, invoice, maxFee, outgoingChanIds, paymentTimeout,
pluginType, reportPluginResult,
ctx, invoice, maxFee, outgoingChanIds, pluginType,
reportPluginResult,
)
if err != nil {
result.err = err
@ -712,9 +674,8 @@ func (s *loopOutSwap) payInvoice(ctx context.Context, invoice string,
// payInvoiceAsync is the asynchronously executed part of paying an invoice.
func (s *loopOutSwap) payInvoiceAsync(ctx context.Context,
invoice string, maxFee btcutil.Amount,
outgoingChanIds loopdb.ChannelSet, paymentTimeout time.Duration,
pluginType RoutingPluginType, reportPluginResult bool) (
*lndclient.PaymentStatus, error) {
outgoingChanIds loopdb.ChannelSet, pluginType RoutingPluginType,
reportPluginResult bool) (*lndclient.PaymentStatus, error) {
// Extract hash from payment request. Unfortunately the request
// components aren't available directly.
@ -727,7 +688,7 @@ func (s *loopOutSwap) payInvoiceAsync(ctx context.Context,
}
maxRetries := 1
totalPaymentTimeout := s.executeConfig.totalPaymentTimeout
paymentTimeout := s.executeConfig.totalPaymentTimeout
// Attempt to acquire and initialize the routing plugin.
routingPlugin, err := AcquireRoutingPlugin(
@ -742,30 +703,8 @@ func (s *loopOutSwap) payInvoiceAsync(ctx context.Context,
pluginType, hash.String())
maxRetries = s.executeConfig.maxPaymentRetries
// If not set, default to the per payment timeout to the total
// payment timeout divied by the configured maximum retries.
if paymentTimeout == 0 {
paymentTimeout = totalPaymentTimeout /
time.Duration(maxRetries)
}
// If the payment timeout is too long, we need to adjust the
// number of retries to ensure we don't exceed the total
// payment timeout.
if paymentTimeout*time.Duration(maxRetries) >
totalPaymentTimeout {
maxRetries = int(totalPaymentTimeout / paymentTimeout)
s.log.Infof("Adjusted max routing plugin retries to "+
"%v to stay within total payment timeout",
maxRetries)
}
paymentTimeout /= time.Duration(maxRetries)
defer ReleaseRoutingPlugin(ctx)
} else if paymentTimeout == 0 {
// If not set, default the payment timeout to the total payment
// timeout.
paymentTimeout = totalPaymentTimeout
}
req := lndclient.SendPaymentRequest{
@ -976,7 +915,7 @@ func (s *loopOutSwap) waitForConfirmedHtlc(globalCtx context.Context) (
case result := <-s.swapPaymentChan:
s.swapPaymentChan = nil
err := s.handlePaymentResult(result, true)
err := s.handlePaymentResult(result)
if err != nil {
return nil, err
}
@ -998,7 +937,7 @@ func (s *loopOutSwap) waitForConfirmedHtlc(globalCtx context.Context) (
case result := <-s.prePaymentChan:
s.prePaymentChan = nil
err := s.handlePaymentResult(result, false)
err := s.handlePaymentResult(result)
if err != nil {
return nil, err
}
@ -1066,9 +1005,9 @@ func (s *loopOutSwap) waitForConfirmedHtlc(globalCtx context.Context) (
// sweep or a server revocation tx.
func (s *loopOutSwap) waitForHtlcSpendConfirmedV2(globalCtx context.Context,
htlcOutpoint wire.OutPoint, htlcValue btcutil.Amount) (
*sweepbatcher.SpendDetail, error) {
*wire.MsgTx, error) {
spendChan := make(chan *sweepbatcher.SpendDetail)
spendChan := make(chan *wire.MsgTx)
spendErrChan := make(chan error, 1)
quitChan := make(chan bool, 1)
@ -1115,10 +1054,10 @@ func (s *loopOutSwap) waitForHtlcSpendConfirmedV2(globalCtx context.Context,
for {
select {
// Htlc spend, break loop.
case spend := <-spendChan:
s.log.Infof("Htlc spend by tx: %v", spend.Tx.TxHash())
case spendTx := <-spendChan:
s.log.Infof("Htlc spend by tx: %v", spendTx.TxHash())
return spend, nil
return spendTx, nil
// Spend notification error.
case err := <-spendErrChan:

@ -17,7 +17,6 @@ import (
"github.com/lightninglabs/loop/sweepbatcher"
"github.com/lightninglabs/loop/test"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/zpay32"
"github.com/stretchr/testify/require"
@ -299,15 +298,10 @@ func testCustomSweepConfTarget(t *testing.T) {
batcherStore := sweepbatcher.NewStoreMock()
sweepStore, err := sweepbatcher.NewSweepFetcherFromSwapStore(
cfg.store, lnd.ChainParams,
)
require.NoError(t, err)
batcher := sweepbatcher.NewBatcher(
lnd.WalletKit, lnd.ChainNotifier, lnd.Signer,
mockMuSig2SignSweep, mockVerifySchnorrSigSuccess,
lnd.ChainParams, batcherStore, sweepStore,
lnd.ChainParams, batcherStore, cfg.store,
)
tctx, cancel := context.WithCancel(context.Background())
@ -429,7 +423,7 @@ func testCustomSweepConfTarget(t *testing.T) {
)
require.NoError(t, err, "unable to retrieve fee estimate")
minFee := feeRate.FeeForWeight(lntypes.WeightUnit(weight))
minFee := feeRate.FeeForWeight(weight)
// Just an estimate that works to sanity check fee upper bound.
maxFee := btcutil.Amount(float64(minFee) * 1.5)
@ -537,15 +531,10 @@ func testPreimagePush(t *testing.T) {
batcherStore := sweepbatcher.NewStoreMock()
sweepStore, err := sweepbatcher.NewSweepFetcherFromSwapStore(
cfg.store, lnd.ChainParams,
)
require.NoError(t, err)
batcher := sweepbatcher.NewBatcher(
lnd.WalletKit, lnd.ChainNotifier, lnd.Signer,
mockMuSig2SignSweep, mockVerifySchnorrSigSuccess,
lnd.ChainParams, batcherStore, sweepStore,
lnd.ChainParams, batcherStore, cfg.store,
)
tctx, cancel := context.WithCancel(context.Background())
@ -963,15 +952,10 @@ func TestLoopOutMuSig2Sweep(t *testing.T) {
batcherStore := sweepbatcher.NewStoreMock()
sweepStore, err := sweepbatcher.NewSweepFetcherFromSwapStore(
cfg.store, lnd.ChainParams,
)
require.NoError(t, err)
batcher := sweepbatcher.NewBatcher(
lnd.WalletKit, lnd.ChainNotifier, lnd.Signer,
mockMuSig2SignSweep, mockVerifySchnorrSigSuccess,
lnd.ChainParams, batcherStore, sweepStore,
lnd.ChainParams, batcherStore, cfg.store,
)
tctx, cancel := context.WithCancel(context.Background())

@ -1,9 +1,9 @@
FROM golang:1.21.9-bookworm
FROM golang:1.16.3-buster
RUN apt-get update && apt-get install -y \
git \
protobuf-compiler='3.21.12*' \
clang-format='1:14.0*'
protobuf-compiler='3.6*' \
clang-format='1:7.0*'
# We don't want any default values for these variables to make sure they're
# explicitly provided by parsing the go.mod file. Otherwise we might forget to
@ -13,19 +13,14 @@ ARG GRPC_GATEWAY_VERSION
ENV PROTOC_GEN_GO_GRPC_VERSION="v1.1.0"
ENV FALAFEL_VERSION="v0.9.1"
ENV GOCACHE=/tmp/build/.cache
ENV GOMODCACHE=/tmp/build/.modcache
RUN cd /tmp \
&& mkdir -p /tmp/build/.cache \
&& mkdir -p /tmp/build/.modcache \
&& go install google.golang.org/protobuf/cmd/protoc-gen-go@${PROTOBUF_VERSION} \
&& go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@${PROTOC_GEN_GO_GRPC_VERSION} \
&& go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway@${GRPC_GATEWAY_VERSION} \
&& go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2@${GRPC_GATEWAY_VERSION} \
&& go install github.com/lightninglabs/falafel@${FALAFEL_VERSION} \
&& go install golang.org/x/tools/cmd/goimports@v0.1.7 \
&& chmod -R 777 /tmp/build/
&& export GO111MODULE=on \
&& go get google.golang.org/protobuf/cmd/protoc-gen-go@${PROTOBUF_VERSION} \
&& go get google.golang.org/grpc/cmd/protoc-gen-go-grpc@${PROTOC_GEN_GO_GRPC_VERSION} \
&& go get github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway@${GRPC_GATEWAY_VERSION} \
&& go get github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2@${GRPC_GATEWAY_VERSION} \
&& go get github.com/lightninglabs/falafel@${FALAFEL_VERSION}
WORKDIR /build

File diff suppressed because it is too large Load Diff

@ -433,38 +433,20 @@ func local_request_SwapClient_Probe_0(ctx context.Context, marshaler runtime.Mar
}
func request_SwapClient_GetL402Tokens_0(ctx context.Context, marshaler runtime.Marshaler, client SwapClientClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
func request_SwapClient_GetLsatTokens_0(ctx context.Context, marshaler runtime.Marshaler, client SwapClientClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq TokensRequest
var metadata runtime.ServerMetadata
msg, err := client.GetL402Tokens(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
msg, err := client.GetLsatTokens(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_SwapClient_GetL402Tokens_0(ctx context.Context, marshaler runtime.Marshaler, server SwapClientServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
func local_request_SwapClient_GetLsatTokens_0(ctx context.Context, marshaler runtime.Marshaler, server SwapClientServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq TokensRequest
var metadata runtime.ServerMetadata
msg, err := server.GetL402Tokens(ctx, &protoReq)
return msg, metadata, err
}
func request_SwapClient_GetL402Tokens_1(ctx context.Context, marshaler runtime.Marshaler, client SwapClientClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq TokensRequest
var metadata runtime.ServerMetadata
msg, err := client.GetL402Tokens(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_SwapClient_GetL402Tokens_1(ctx context.Context, marshaler runtime.Marshaler, server SwapClientServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq TokensRequest
var metadata runtime.ServerMetadata
msg, err := server.GetL402Tokens(ctx, &protoReq)
msg, err := server.GetLsatTokens(ctx, &protoReq)
return msg, metadata, err
}
@ -788,7 +770,7 @@ func RegisterSwapClientHandlerServer(ctx context.Context, mux *runtime.ServeMux,
})
mux.Handle("GET", pattern_SwapClient_GetL402Tokens_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
mux.Handle("GET", pattern_SwapClient_GetLsatTokens_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
@ -796,12 +778,12 @@ func RegisterSwapClientHandlerServer(ctx context.Context, mux *runtime.ServeMux,
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/looprpc.SwapClient/GetL402Tokens", runtime.WithHTTPPathPattern("/v1/l402/tokens"))
annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/looprpc.SwapClient/GetLsatTokens", runtime.WithHTTPPathPattern("/v1/lsat/tokens"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_SwapClient_GetL402Tokens_0(annotatedContext, inboundMarshaler, server, req, pathParams)
resp, md, err := local_request_SwapClient_GetLsatTokens_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
@ -809,32 +791,7 @@ func RegisterSwapClientHandlerServer(ctx context.Context, mux *runtime.ServeMux,
return
}
forward_SwapClient_GetL402Tokens_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("GET", pattern_SwapClient_GetL402Tokens_1, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/looprpc.SwapClient/GetL402Tokens", runtime.WithHTTPPathPattern("/v1/lsat/tokens"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_SwapClient_GetL402Tokens_1(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_SwapClient_GetL402Tokens_1(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
forward_SwapClient_GetLsatTokens_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
@ -1177,47 +1134,25 @@ func RegisterSwapClientHandlerClient(ctx context.Context, mux *runtime.ServeMux,
})
mux.Handle("GET", pattern_SwapClient_GetL402Tokens_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
mux.Handle("GET", pattern_SwapClient_GetLsatTokens_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/looprpc.SwapClient/GetL402Tokens", runtime.WithHTTPPathPattern("/v1/l402/tokens"))
annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/looprpc.SwapClient/GetLsatTokens", runtime.WithHTTPPathPattern("/v1/lsat/tokens"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_SwapClient_GetL402Tokens_0(annotatedContext, inboundMarshaler, client, req, pathParams)
resp, md, err := request_SwapClient_GetLsatTokens_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_SwapClient_GetL402Tokens_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("GET", pattern_SwapClient_GetL402Tokens_1, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/looprpc.SwapClient/GetL402Tokens", runtime.WithHTTPPathPattern("/v1/lsat/tokens"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_SwapClient_GetL402Tokens_1(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_SwapClient_GetL402Tokens_1(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
forward_SwapClient_GetLsatTokens_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
@ -1331,9 +1266,7 @@ var (
pattern_SwapClient_Probe_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"v1", "loop", "in", "probe", "amt"}, ""))
pattern_SwapClient_GetL402Tokens_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "l402", "tokens"}, ""))
pattern_SwapClient_GetL402Tokens_1 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "lsat", "tokens"}, ""))
pattern_SwapClient_GetLsatTokens_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "lsat", "tokens"}, ""))
pattern_SwapClient_GetInfo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "loop", "info"}, ""))
@ -1363,9 +1296,7 @@ var (
forward_SwapClient_Probe_0 = runtime.ForwardResponseMessage
forward_SwapClient_GetL402Tokens_0 = runtime.ForwardResponseMessage
forward_SwapClient_GetL402Tokens_1 = runtime.ForwardResponseMessage
forward_SwapClient_GetLsatTokens_0 = runtime.ForwardResponseMessage
forward_SwapClient_GetInfo_0 = runtime.ForwardResponseMessage

@ -76,16 +76,7 @@ service SwapClient {
rpc Probe (ProbeRequest) returns (ProbeResponse);
/* loop: `listauth`
GetL402Tokens returns all L402 tokens the daemon ever paid for.
*/
rpc GetL402Tokens (TokensRequest) returns (TokensResponse);
/*
Deprecated: use GetL402Tokens.
This API is provided to maintain backward compatibility with gRPC clients
(e.g. `loop listauth`, Terminal Web, RTL).
Type LsatToken used by GetLsatTokens in the past was renamed to L402Token,
but this does not affect binary encoding, so we can use type L402Token here.
GetLsatTokens returns all LSAT tokens the daemon ever paid for.
*/
rpc GetLsatTokens (TokensRequest) returns (TokensResponse);
@ -136,13 +127,6 @@ service SwapClient {
*/
rpc InstantOutQuote (InstantOutQuoteRequest)
returns (InstantOutQuoteResponse);
/* loop: `listinstantouts`
ListInstantOuts returns a list of all currently known instant out swaps and
their current status.
*/
rpc ListInstantOuts (ListInstantOutsRequest)
returns (ListInstantOutsResponse);
}
message LoopOutRequest {
@ -278,13 +262,6 @@ message LoopOutRequest {
be equal to the sum of the amounts of the reservations.
*/
repeated bytes reservation_ids = 18;
/*
The timeout in seconds to use for off-chain payments. Note that the swap
payment is attempted multiple times where each attempt will set this value
as the timeout for the payment.
*/
uint32 payment_timeout = 19;
}
/*
@ -827,10 +804,10 @@ message TokensResponse {
/*
List of all tokens the daemon knows of, including old/expired tokens.
*/
repeated L402Token tokens = 1;
repeated LsatToken tokens = 1;
}
message L402Token {
message LsatToken {
/*
The base macaroon that was baked by the auth server.
*/
@ -1355,13 +1332,7 @@ message InstantOutRequest {
channel(s) that will be used are selected based on the lowest routing fee
for the swap payment to the server.
*/
repeated uint64 outgoing_chan_set = 2;
/*
An optional address to sweep the onchain funds to. If not set, the funds
will be swept to the wallet's internal address.
*/
string dest_addr = 3;
repeated uint64 outgoing_chan_set = 11;
}
message InstantOutResponse {
@ -1402,41 +1373,4 @@ message InstantOutQuoteResponse {
Sweep.
*/
int64 sweep_fee_sat = 2;
}
message ListInstantOutsRequest {
}
message ListInstantOutsResponse {
/*
The list of all currently known instant out swaps and their status.
*/
repeated InstantOut swaps = 1;
}
message InstantOut {
/*
The swap hash that identifies this swap.
*/
bytes swap_hash = 1;
/*
The state the swap is in.
*/
string state = 2;
/*
The amount of the swap.
*/
uint64 amount = 3;
/*
The used reservations for the swap.
*/
repeated bytes reservation_ids = 4;
/*
The sweep transaction id of the swap.
*/
string sweep_tx_id = 5;
}
}

@ -39,29 +39,6 @@
]
}
},
"/v1/l402/tokens": {
"get": {
"summary": "loop: `listauth`\nGetL402Tokens returns all L402 tokens the daemon ever paid for.",
"operationId": "SwapClient_GetL402Tokens",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/looprpcTokensResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/rpcStatus"
}
}
},
"tags": [
"SwapClient"
]
}
},
"/v1/liquidity/params": {
"get": {
"summary": "loop: `getparams`\nGetLiquidityParams gets the parameters that the daemon's liquidity manager\nis currently configured with. This may be nil if nothing is configured.\n[EXPERIMENTAL]: endpoint is subject to change.",
@ -541,8 +518,8 @@
},
"/v1/lsat/tokens": {
"get": {
"summary": "loop: `listauth`\nGetL402Tokens returns all L402 tokens the daemon ever paid for.",
"operationId": "SwapClient_GetL402Tokens2",
"summary": "loop: `listauth`\nGetLsatTokens returns all LSAT tokens the daemon ever paid for.",
"operationId": "SwapClient_GetLsatTokens",
"responses": {
"200": {
"description": "A successful response.",
@ -783,37 +760,6 @@
}
}
},
"looprpcInstantOut": {
"type": "object",
"properties": {
"swap_hash": {
"type": "string",
"format": "byte",
"description": "The swap hash that identifies this swap."
},
"state": {
"type": "string",
"description": "The state the swap is in."
},
"amount": {
"type": "string",
"format": "uint64",
"description": "The amount of the swap."
},
"reservation_ids": {
"type": "array",
"items": {
"type": "string",
"format": "byte"
},
"description": "The used reservations for the swap."
},
"sweep_tx_id": {
"type": "string",
"description": "The sweep transaction id of the swap."
}
}
},
"looprpcInstantOutQuoteResponse": {
"type": "object",
"properties": {
@ -847,53 +793,6 @@
}
}
},
"looprpcL402Token": {
"type": "object",
"properties": {
"base_macaroon": {
"type": "string",
"format": "byte",
"description": "The base macaroon that was baked by the auth server."
},
"payment_hash": {
"type": "string",
"format": "byte",
"description": "The payment hash of the payment that was paid to obtain the token."
},
"payment_preimage": {
"type": "string",
"format": "byte",
"description": "The preimage of the payment hash, knowledge of this is proof that the\npayment has been paid. If the preimage is set to all zeros, this means the\npayment is still pending and the token is not yet fully valid."
},
"amount_paid_msat": {
"type": "string",
"format": "int64",
"description": "The amount of millisatoshis that was paid to get the token."
},
"routing_fee_paid_msat": {
"type": "string",
"format": "int64",
"description": "The amount of millisatoshis paid in routing fee to pay for the token."
},
"time_created": {
"type": "string",
"format": "int64",
"description": "The creation time of the token as UNIX timestamp in seconds."
},
"expired": {
"type": "boolean",
"description": "Indicates whether the token is expired or still valid."
},
"storage_name": {
"type": "string",
"description": "Identifying attribute of this token in the store. Currently represents the\nfile name of the token where it's stored on the file system."
},
"id": {
"type": "string",
"description": "The l402 ID of the token."
}
}
},
"looprpcLiquidityParameters": {
"type": "object",
"properties": {
@ -1057,18 +956,6 @@
],
"default": "UNKNOWN"
},
"looprpcListInstantOutsResponse": {
"type": "object",
"properties": {
"swaps": {
"type": "array",
"items": {
"$ref": "#/definitions/looprpcInstantOut"
},
"description": "The list of all currently known instant out swaps and their status."
}
}
},
"looprpcListReservationsResponse": {
"type": "object",
"properties": {
@ -1268,11 +1155,6 @@
"format": "byte"
},
"description": "The reservations to use for the swap. If this field is set, loop will try\nto use the instant out flow using the given reservations. If the\nreservations are not sufficient, the swap will fail. The swap amount must\nbe equal to the sum of the amounts of the reservations."
},
"payment_timeout": {
"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."
}
}
},
@ -1306,6 +1188,53 @@
}
}
},
"looprpcLsatToken": {
"type": "object",
"properties": {
"base_macaroon": {
"type": "string",
"format": "byte",
"description": "The base macaroon that was baked by the auth server."
},
"payment_hash": {
"type": "string",
"format": "byte",
"description": "The payment hash of the payment that was paid to obtain the token."
},
"payment_preimage": {
"type": "string",
"format": "byte",
"description": "The preimage of the payment hash, knowledge of this is proof that the\npayment has been paid. If the preimage is set to all zeros, this means the\npayment is still pending and the token is not yet fully valid."
},
"amount_paid_msat": {
"type": "string",
"format": "int64",
"description": "The amount of millisatoshis that was paid to get the token."
},
"routing_fee_paid_msat": {
"type": "string",
"format": "int64",
"description": "The amount of millisatoshis paid in routing fee to pay for the token."
},
"time_created": {
"type": "string",
"format": "int64",
"description": "The creation time of the token as UNIX timestamp in seconds."
},
"expired": {
"type": "boolean",
"description": "Indicates whether the token is expired or still valid."
},
"storage_name": {
"type": "string",
"description": "Identifying attribute of this token in the store. Currently represents the\nfile name of the token where it's stored on the file system."
},
"id": {
"type": "string",
"description": "The l402 ID of the token."
}
}
},
"looprpcOutQuoteResponse": {
"type": "object",
"properties": {
@ -1562,7 +1491,7 @@
"tokens": {
"type": "array",
"items": {
"$ref": "#/definitions/looprpcL402Token"
"$ref": "#/definitions/looprpcLsatToken"
},
"description": "List of all tokens the daemon knows of, including old/expired tokens."
}

@ -26,10 +26,8 @@ http:
get: "/v1/loop/in/probe/{amt}"
- selector: looprpc.SwapClient.GetInfo
get: "/v1/loop/info"
- selector: looprpc.SwapClient.GetL402Tokens
get: "/v1/l402/tokens"
additional_bindings:
- get: "/v1/lsat/tokens"
- selector: looprpc.SwapClient.GetLsatTokens
get: "/v1/lsat/tokens"
- selector: looprpc.SwapClient.GetLiquidityParams
get: "/v1/liquidity/params"
- selector: looprpc.SwapClient.SetLiquidityParams

@ -19,89 +19,80 @@ const _ = grpc.SupportPackageIsVersion7
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type SwapClientClient interface {
// loop: `out`
// LoopOut initiates an loop out swap with the given parameters. The call
// returns after the swap has been set up with the swap server. From that
// point onwards, progress can be tracked via the SwapStatus stream that is
// returned from Monitor().
//LoopOut initiates an loop out swap with the given parameters. The call
//returns after the swap has been set up with the swap server. From that
//point onwards, progress can be tracked via the SwapStatus stream that is
//returned from Monitor().
LoopOut(ctx context.Context, in *LoopOutRequest, opts ...grpc.CallOption) (*SwapResponse, error)
// loop: `in`
// LoopIn initiates a loop in swap with the given parameters. The call
// returns after the swap has been set up with the swap server. From that
// point onwards, progress can be tracked via the SwapStatus stream
// that is returned from Monitor().
//LoopIn initiates a loop in swap with the given parameters. The call
//returns after the swap has been set up with the swap server. From that
//point onwards, progress can be tracked via the SwapStatus stream
//that is returned from Monitor().
LoopIn(ctx context.Context, in *LoopInRequest, opts ...grpc.CallOption) (*SwapResponse, error)
// loop: `monitor`
// Monitor will return a stream of swap updates for currently active swaps.
//Monitor will return a stream of swap updates for currently active swaps.
Monitor(ctx context.Context, in *MonitorRequest, opts ...grpc.CallOption) (SwapClient_MonitorClient, error)
// loop: `listswaps`
// ListSwaps returns a list of all currently known swaps and their current
// status.
//ListSwaps returns a list of all currently known swaps and their current
//status.
ListSwaps(ctx context.Context, in *ListSwapsRequest, opts ...grpc.CallOption) (*ListSwapsResponse, error)
// loop: `swapinfo`
// SwapInfo returns all known details about a single swap.
//SwapInfo returns all known details about a single swap.
SwapInfo(ctx context.Context, in *SwapInfoRequest, opts ...grpc.CallOption) (*SwapStatus, error)
// loop: `abandonswap`
// AbandonSwap allows the client to abandon a swap.
//AbandonSwap allows the client to abandon a swap.
AbandonSwap(ctx context.Context, in *AbandonSwapRequest, opts ...grpc.CallOption) (*AbandonSwapResponse, error)
// loop: `terms`
// LoopOutTerms returns the terms that the server enforces for a loop out swap.
//LoopOutTerms returns the terms that the server enforces for a loop out swap.
LoopOutTerms(ctx context.Context, in *TermsRequest, opts ...grpc.CallOption) (*OutTermsResponse, error)
// loop: `quote`
// LoopOutQuote returns a quote for a loop out swap with the provided
// parameters.
//LoopOutQuote returns a quote for a loop out swap with the provided
//parameters.
LoopOutQuote(ctx context.Context, in *QuoteRequest, opts ...grpc.CallOption) (*OutQuoteResponse, error)
// loop: `terms`
// GetTerms returns the terms that the server enforces for swaps.
//GetTerms returns the terms that the server enforces for swaps.
GetLoopInTerms(ctx context.Context, in *TermsRequest, opts ...grpc.CallOption) (*InTermsResponse, error)
// loop: `quote`
// GetQuote returns a quote for a swap with the provided parameters.
//GetQuote returns a quote for a swap with the provided parameters.
GetLoopInQuote(ctx context.Context, in *QuoteRequest, opts ...grpc.CallOption) (*InQuoteResponse, error)
// Probe asks he sever to probe the route to us to have a better upfront
// estimate about routing fees when loopin-in.
//
//Probe asks he sever to probe the route to us to have a better upfront
//estimate about routing fees when loopin-in.
Probe(ctx context.Context, in *ProbeRequest, opts ...grpc.CallOption) (*ProbeResponse, error)
// loop: `listauth`
// GetL402Tokens returns all L402 tokens the daemon ever paid for.
GetL402Tokens(ctx context.Context, in *TokensRequest, opts ...grpc.CallOption) (*TokensResponse, error)
// Deprecated: use GetL402Tokens.
// This API is provided to maintain backward compatibility with gRPC clients
// (e.g. `loop listauth`, Terminal Web, RTL).
// Type LsatToken used by GetLsatTokens in the past was renamed to L402Token,
// but this does not affect binary encoding, so we can use type L402Token here.
//GetLsatTokens returns all LSAT tokens the daemon ever paid for.
GetLsatTokens(ctx context.Context, in *TokensRequest, opts ...grpc.CallOption) (*TokensResponse, error)
// loop: `getinfo`
// GetInfo gets basic information about the loop daemon.
//GetInfo gets basic information about the loop daemon.
GetInfo(ctx context.Context, in *GetInfoRequest, opts ...grpc.CallOption) (*GetInfoResponse, error)
// loop: `getparams`
// GetLiquidityParams gets the parameters that the daemon's liquidity manager
// is currently configured with. This may be nil if nothing is configured.
// [EXPERIMENTAL]: endpoint is subject to change.
//GetLiquidityParams gets the parameters that the daemon's liquidity manager
//is currently configured with. This may be nil if nothing is configured.
//[EXPERIMENTAL]: endpoint is subject to change.
GetLiquidityParams(ctx context.Context, in *GetLiquidityParamsRequest, opts ...grpc.CallOption) (*LiquidityParameters, error)
// loop: `setparams`
// SetLiquidityParams sets a new set of parameters for the daemon's liquidity
// manager. Note that the full set of parameters must be provided, because
// this call fully overwrites our existing parameters.
// [EXPERIMENTAL]: endpoint is subject to change.
//SetLiquidityParams sets a new set of parameters for the daemon's liquidity
//manager. Note that the full set of parameters must be provided, because
//this call fully overwrites our existing parameters.
//[EXPERIMENTAL]: endpoint is subject to change.
SetLiquidityParams(ctx context.Context, in *SetLiquidityParamsRequest, opts ...grpc.CallOption) (*SetLiquidityParamsResponse, error)
// loop: `suggestswaps`
// SuggestSwaps returns a list of recommended swaps based on the current
// state of your node's channels and it's liquidity manager parameters.
// Note that only loop out suggestions are currently supported.
// [EXPERIMENTAL]: endpoint is subject to change.
//SuggestSwaps returns a list of recommended swaps based on the current
//state of your node's channels and it's liquidity manager parameters.
//Note that only loop out suggestions are currently supported.
//[EXPERIMENTAL]: endpoint is subject to change.
SuggestSwaps(ctx context.Context, in *SuggestSwapsRequest, opts ...grpc.CallOption) (*SuggestSwapsResponse, error)
// loop: `listreservations`
// ListReservations returns a list of all reservations the server opened to us.
//ListReservations returns a list of all reservations the server opened to us.
ListReservations(ctx context.Context, in *ListReservationsRequest, opts ...grpc.CallOption) (*ListReservationsResponse, error)
// loop: `instantout`
// InstantOut initiates an instant out swap with the given parameters.
//InstantOut initiates an instant out swap with the given parameters.
InstantOut(ctx context.Context, in *InstantOutRequest, opts ...grpc.CallOption) (*InstantOutResponse, error)
// loop: `instantoutquote`
// InstantOutQuote returns a quote for an instant out swap with the provided
// parameters.
//InstantOutQuote returns a quote for an instant out swap with the provided
//parameters.
InstantOutQuote(ctx context.Context, in *InstantOutQuoteRequest, opts ...grpc.CallOption) (*InstantOutQuoteResponse, error)
// loop: `listinstantouts`
// ListInstantOuts returns a list of all currently known instant out swaps and
// their current status.
ListInstantOuts(ctx context.Context, in *ListInstantOutsRequest, opts ...grpc.CallOption) (*ListInstantOutsResponse, error)
}
type swapClientClient struct {
@ -234,15 +225,6 @@ func (c *swapClientClient) Probe(ctx context.Context, in *ProbeRequest, opts ...
return out, nil
}
func (c *swapClientClient) GetL402Tokens(ctx context.Context, in *TokensRequest, opts ...grpc.CallOption) (*TokensResponse, error) {
out := new(TokensResponse)
err := c.cc.Invoke(ctx, "/looprpc.SwapClient/GetL402Tokens", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *swapClientClient) GetLsatTokens(ctx context.Context, in *TokensRequest, opts ...grpc.CallOption) (*TokensResponse, error) {
out := new(TokensResponse)
err := c.cc.Invoke(ctx, "/looprpc.SwapClient/GetLsatTokens", in, out, opts...)
@ -315,103 +297,85 @@ func (c *swapClientClient) InstantOutQuote(ctx context.Context, in *InstantOutQu
return out, nil
}
func (c *swapClientClient) ListInstantOuts(ctx context.Context, in *ListInstantOutsRequest, opts ...grpc.CallOption) (*ListInstantOutsResponse, error) {
out := new(ListInstantOutsResponse)
err := c.cc.Invoke(ctx, "/looprpc.SwapClient/ListInstantOuts", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// SwapClientServer is the server API for SwapClient service.
// All implementations must embed UnimplementedSwapClientServer
// for forward compatibility
type SwapClientServer interface {
// loop: `out`
// LoopOut initiates an loop out swap with the given parameters. The call
// returns after the swap has been set up with the swap server. From that
// point onwards, progress can be tracked via the SwapStatus stream that is
// returned from Monitor().
//LoopOut initiates an loop out swap with the given parameters. The call
//returns after the swap has been set up with the swap server. From that
//point onwards, progress can be tracked via the SwapStatus stream that is
//returned from Monitor().
LoopOut(context.Context, *LoopOutRequest) (*SwapResponse, error)
// loop: `in`
// LoopIn initiates a loop in swap with the given parameters. The call
// returns after the swap has been set up with the swap server. From that
// point onwards, progress can be tracked via the SwapStatus stream
// that is returned from Monitor().
//LoopIn initiates a loop in swap with the given parameters. The call
//returns after the swap has been set up with the swap server. From that
//point onwards, progress can be tracked via the SwapStatus stream
//that is returned from Monitor().
LoopIn(context.Context, *LoopInRequest) (*SwapResponse, error)
// loop: `monitor`
// Monitor will return a stream of swap updates for currently active swaps.
//Monitor will return a stream of swap updates for currently active swaps.
Monitor(*MonitorRequest, SwapClient_MonitorServer) error
// loop: `listswaps`
// ListSwaps returns a list of all currently known swaps and their current
// status.
//ListSwaps returns a list of all currently known swaps and their current
//status.
ListSwaps(context.Context, *ListSwapsRequest) (*ListSwapsResponse, error)
// loop: `swapinfo`
// SwapInfo returns all known details about a single swap.
//SwapInfo returns all known details about a single swap.
SwapInfo(context.Context, *SwapInfoRequest) (*SwapStatus, error)
// loop: `abandonswap`
// AbandonSwap allows the client to abandon a swap.
//AbandonSwap allows the client to abandon a swap.
AbandonSwap(context.Context, *AbandonSwapRequest) (*AbandonSwapResponse, error)
// loop: `terms`
// LoopOutTerms returns the terms that the server enforces for a loop out swap.
//LoopOutTerms returns the terms that the server enforces for a loop out swap.
LoopOutTerms(context.Context, *TermsRequest) (*OutTermsResponse, error)
// loop: `quote`
// LoopOutQuote returns a quote for a loop out swap with the provided
// parameters.
//LoopOutQuote returns a quote for a loop out swap with the provided
//parameters.
LoopOutQuote(context.Context, *QuoteRequest) (*OutQuoteResponse, error)
// loop: `terms`
// GetTerms returns the terms that the server enforces for swaps.
//GetTerms returns the terms that the server enforces for swaps.
GetLoopInTerms(context.Context, *TermsRequest) (*InTermsResponse, error)
// loop: `quote`
// GetQuote returns a quote for a swap with the provided parameters.
//GetQuote returns a quote for a swap with the provided parameters.
GetLoopInQuote(context.Context, *QuoteRequest) (*InQuoteResponse, error)
// Probe asks he sever to probe the route to us to have a better upfront
// estimate about routing fees when loopin-in.
//
//Probe asks he sever to probe the route to us to have a better upfront
//estimate about routing fees when loopin-in.
Probe(context.Context, *ProbeRequest) (*ProbeResponse, error)
// loop: `listauth`
// GetL402Tokens returns all L402 tokens the daemon ever paid for.
GetL402Tokens(context.Context, *TokensRequest) (*TokensResponse, error)
// Deprecated: use GetL402Tokens.
// This API is provided to maintain backward compatibility with gRPC clients
// (e.g. `loop listauth`, Terminal Web, RTL).
// Type LsatToken used by GetLsatTokens in the past was renamed to L402Token,
// but this does not affect binary encoding, so we can use type L402Token here.
//GetLsatTokens returns all LSAT tokens the daemon ever paid for.
GetLsatTokens(context.Context, *TokensRequest) (*TokensResponse, error)
// loop: `getinfo`
// GetInfo gets basic information about the loop daemon.
//GetInfo gets basic information about the loop daemon.
GetInfo(context.Context, *GetInfoRequest) (*GetInfoResponse, error)
// loop: `getparams`
// GetLiquidityParams gets the parameters that the daemon's liquidity manager
// is currently configured with. This may be nil if nothing is configured.
// [EXPERIMENTAL]: endpoint is subject to change.
//GetLiquidityParams gets the parameters that the daemon's liquidity manager
//is currently configured with. This may be nil if nothing is configured.
//[EXPERIMENTAL]: endpoint is subject to change.
GetLiquidityParams(context.Context, *GetLiquidityParamsRequest) (*LiquidityParameters, error)
// loop: `setparams`
// SetLiquidityParams sets a new set of parameters for the daemon's liquidity
// manager. Note that the full set of parameters must be provided, because
// this call fully overwrites our existing parameters.
// [EXPERIMENTAL]: endpoint is subject to change.
//SetLiquidityParams sets a new set of parameters for the daemon's liquidity
//manager. Note that the full set of parameters must be provided, because
//this call fully overwrites our existing parameters.
//[EXPERIMENTAL]: endpoint is subject to change.
SetLiquidityParams(context.Context, *SetLiquidityParamsRequest) (*SetLiquidityParamsResponse, error)
// loop: `suggestswaps`
// SuggestSwaps returns a list of recommended swaps based on the current
// state of your node's channels and it's liquidity manager parameters.
// Note that only loop out suggestions are currently supported.
// [EXPERIMENTAL]: endpoint is subject to change.
//SuggestSwaps returns a list of recommended swaps based on the current
//state of your node's channels and it's liquidity manager parameters.
//Note that only loop out suggestions are currently supported.
//[EXPERIMENTAL]: endpoint is subject to change.
SuggestSwaps(context.Context, *SuggestSwapsRequest) (*SuggestSwapsResponse, error)
// loop: `listreservations`
// ListReservations returns a list of all reservations the server opened to us.
//ListReservations returns a list of all reservations the server opened to us.
ListReservations(context.Context, *ListReservationsRequest) (*ListReservationsResponse, error)
// loop: `instantout`
// InstantOut initiates an instant out swap with the given parameters.
//InstantOut initiates an instant out swap with the given parameters.
InstantOut(context.Context, *InstantOutRequest) (*InstantOutResponse, error)
// loop: `instantoutquote`
// InstantOutQuote returns a quote for an instant out swap with the provided
// parameters.
//InstantOutQuote returns a quote for an instant out swap with the provided
//parameters.
InstantOutQuote(context.Context, *InstantOutQuoteRequest) (*InstantOutQuoteResponse, error)
// loop: `listinstantouts`
// ListInstantOuts returns a list of all currently known instant out swaps and
// their current status.
ListInstantOuts(context.Context, *ListInstantOutsRequest) (*ListInstantOutsResponse, error)
mustEmbedUnimplementedSwapClientServer()
}
@ -452,9 +416,6 @@ func (UnimplementedSwapClientServer) GetLoopInQuote(context.Context, *QuoteReque
func (UnimplementedSwapClientServer) Probe(context.Context, *ProbeRequest) (*ProbeResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Probe not implemented")
}
func (UnimplementedSwapClientServer) GetL402Tokens(context.Context, *TokensRequest) (*TokensResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetL402Tokens not implemented")
}
func (UnimplementedSwapClientServer) GetLsatTokens(context.Context, *TokensRequest) (*TokensResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetLsatTokens not implemented")
}
@ -479,9 +440,6 @@ func (UnimplementedSwapClientServer) InstantOut(context.Context, *InstantOutRequ
func (UnimplementedSwapClientServer) InstantOutQuote(context.Context, *InstantOutQuoteRequest) (*InstantOutQuoteResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method InstantOutQuote not implemented")
}
func (UnimplementedSwapClientServer) ListInstantOuts(context.Context, *ListInstantOutsRequest) (*ListInstantOutsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListInstantOuts not implemented")
}
func (UnimplementedSwapClientServer) mustEmbedUnimplementedSwapClientServer() {}
// UnsafeSwapClientServer may be embedded to opt out of forward compatibility for this service.
@ -696,24 +654,6 @@ func _SwapClient_Probe_Handler(srv interface{}, ctx context.Context, dec func(in
return interceptor(ctx, in, info, handler)
}
func _SwapClient_GetL402Tokens_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(TokensRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(SwapClientServer).GetL402Tokens(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/looprpc.SwapClient/GetL402Tokens",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(SwapClientServer).GetL402Tokens(ctx, req.(*TokensRequest))
}
return interceptor(ctx, in, info, handler)
}
func _SwapClient_GetLsatTokens_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(TokensRequest)
if err := dec(in); err != nil {
@ -858,24 +798,6 @@ func _SwapClient_InstantOutQuote_Handler(srv interface{}, ctx context.Context, d
return interceptor(ctx, in, info, handler)
}
func _SwapClient_ListInstantOuts_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ListInstantOutsRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(SwapClientServer).ListInstantOuts(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/looprpc.SwapClient/ListInstantOuts",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(SwapClientServer).ListInstantOuts(ctx, req.(*ListInstantOutsRequest))
}
return interceptor(ctx, in, info, handler)
}
// SwapClient_ServiceDesc is the grpc.ServiceDesc for SwapClient service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
@ -923,10 +845,6 @@ var SwapClient_ServiceDesc = grpc.ServiceDesc{
MethodName: "Probe",
Handler: _SwapClient_Probe_Handler,
},
{
MethodName: "GetL402Tokens",
Handler: _SwapClient_GetL402Tokens_Handler,
},
{
MethodName: "GetLsatTokens",
Handler: _SwapClient_GetLsatTokens_Handler,
@ -959,10 +877,6 @@ var SwapClient_ServiceDesc = grpc.ServiceDesc{
MethodName: "InstantOutQuote",
Handler: _SwapClient_InstantOutQuote_Handler,
},
{
MethodName: "ListInstantOuts",
Handler: _SwapClient_ListInstantOuts_Handler,
},
},
Streams: []grpc.StreamDesc{
{

@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.33.0
// protoc v3.21.12
// protoc-gen-go v1.31.0
// protoc v3.6.1
// source: debug.proto
package looprpc

@ -18,9 +18,10 @@ const _ = grpc.SupportPackageIsVersion7
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type DebugClient interface {
// ForceAutoLoop is intended for *testing purposes only* and will not work on
// mainnet. This endpoint ticks our autoloop timer, triggering automated
// dispatch of a swap if one is suggested.
//
//ForceAutoLoop is intended for *testing purposes only* and will not work on
//mainnet. This endpoint ticks our autoloop timer, triggering automated
//dispatch of a swap if one is suggested.
ForceAutoLoop(ctx context.Context, in *ForceAutoLoopRequest, opts ...grpc.CallOption) (*ForceAutoLoopResponse, error)
}
@ -45,9 +46,10 @@ func (c *debugClient) ForceAutoLoop(ctx context.Context, in *ForceAutoLoopReques
// All implementations must embed UnimplementedDebugServer
// for forward compatibility
type DebugServer interface {
// ForceAutoLoop is intended for *testing purposes only* and will not work on
// mainnet. This endpoint ticks our autoloop timer, triggering automated
// dispatch of a swap if one is suggested.
//
//ForceAutoLoop is intended for *testing purposes only* and will not work on
//mainnet. This endpoint ticks our autoloop timer, triggering automated
//dispatch of a swap if one is suggested.
ForceAutoLoop(context.Context, *ForceAutoLoopRequest) (*ForceAutoLoopResponse, error)
mustEmbedUnimplementedDebugServer()
}

@ -313,31 +313,6 @@ func RegisterSwapClientJSONCallbacks(registry map[string]func(ctx context.Contex
callback(string(respBytes), nil)
}
registry["looprpc.SwapClient.GetL402Tokens"] = func(ctx context.Context,
conn *grpc.ClientConn, reqJSON string, callback func(string, error)) {
req := &TokensRequest{}
err := marshaler.Unmarshal([]byte(reqJSON), req)
if err != nil {
callback("", err)
return
}
client := NewSwapClientClient(conn)
resp, err := client.GetL402Tokens(ctx, req)
if err != nil {
callback("", err)
return
}
respBytes, err := marshaler.Marshal(resp)
if err != nil {
callback("", err)
return
}
callback(string(respBytes), nil)
}
registry["looprpc.SwapClient.GetLsatTokens"] = func(ctx context.Context,
conn *grpc.ClientConn, reqJSON string, callback func(string, error)) {
@ -537,29 +512,4 @@ func RegisterSwapClientJSONCallbacks(registry map[string]func(ctx context.Contex
}
callback(string(respBytes), nil)
}
registry["looprpc.SwapClient.ListInstantOuts"] = func(ctx context.Context,
conn *grpc.ClientConn, reqJSON string, callback func(string, error)) {
req := &ListInstantOutsRequest{}
err := marshaler.Unmarshal([]byte(reqJSON), req)
if err != nil {
callback("", err)
return
}
client := NewSwapClientClient(conn)
resp, err := client.ListInstantOuts(ctx, req)
if err != nil {
callback("", err)
return
}
respBytes, err := marshaler.Marshal(resp)
if err != nil {
callback("", err)
return
}
callback(string(respBytes), nil)
}
}

@ -130,7 +130,7 @@ services:
- "loopd"
- "--network=regtest"
- "--debuglevel=debug"
- "--server.host=loopserver:11009"
- "--server.host=loopclient:11009"
- "--server.notls"
- "--lnd.host=lndclient:10009"
- "--lnd.macaroonpath=/root/.lnd/data/chain/bitcoin/regtest/admin.macaroon"
@ -142,4 +142,4 @@ networks:
volumes:
bitcoind:
lndserver:
lndclient:
lndclient:

@ -513,7 +513,7 @@ func (r *lowToHighRoutingPlugin) BeforePayment(ctx context.Context,
// Calculate the limit until we'll disable edges. The way we calculate
// this limit is that we take the minimum and maximum fee peers which
// define our fee range. Within this fee range we'll scale linearly
// where each step equals to the range divided by maxAttempts.
// where each step euqals to the range divided by maxAttempts.
minFee := r.nodesByMaxFee[0].fee
maxFee := r.nodesByMaxFee[len(r.nodesByMaxFee)-1].fee
limit := minFee +

@ -86,11 +86,11 @@
; Maximum cost in satoshis that loopd is going to pay for an L402 token
; automatically. Does not include routing fees.
; maxl402cost=1000
; maxlsatcost=1000
; Maximum routing fee in satoshis that we are willing to pay while paying for an
; L402 token.
; maxl402fee=10
; maxlsatfee=10
; The maximum number of payment parts that may be used for a loop out swap.
; loopoutmaxparts=5

@ -16,5 +16,5 @@ docker run \
-e UID=$UID \
-v "$DIR/../:/build" \
-w /build \
sqlc/sqlc:1.25.0 generate
kjconroy/sqlc:1.17.2 generate

@ -42,7 +42,6 @@ type serverMock struct {
swapInvoice string
swapHash lntypes.Hash
prepayHash lntypes.Hash
// preimagePush is a channel that preimage pushes are sent into.
preimagePush chan lntypes.Preimage
@ -82,17 +81,13 @@ func (s *serverMock) NewLoopOutSwap(_ context.Context, swapHash lntypes.Hash,
return nil, errors.New("unexpected test swap amount")
}
s.swapHash = swapHash
swapPayReqString, err := getInvoice(swapHash, s.swapInvoiceAmt,
swapInvoiceDesc)
if err != nil {
return nil, err
}
// Set the prepay hash to be different from the swap hash.
s.prepayHash = swapHash
s.prepayHash[0] ^= 1
prePayReqString, err := getInvoice(s.prepayHash, s.prepayInvoiceAmt,
prePayReqString, err := getInvoice(swapHash, s.prepayInvoiceAmt,
prepayInvoiceDesc)
if err != nil {
return nil, err

@ -1,4 +1,4 @@
package version
package staticaddr
import (
looprpc "github.com/lightninglabs/loop/swapserverrpc"

@ -41,7 +41,7 @@ const (
HtlcV3
)
// HtlcScript defines an interface for the different HTLC implementations.
// htlcScript defines an interface for the different HTLC implementations.
type HtlcScript interface {
// genSuccessWitness returns the success script to spend this htlc with
// the preimage.
@ -63,11 +63,11 @@ type HtlcScript interface {
// MaxSuccessWitnessSize returns the maximum witness size for the
// success case witness.
MaxSuccessWitnessSize() lntypes.WeightUnit
MaxSuccessWitnessSize() int
// MaxTimeoutWitnessSize returns the maximum witness size for the
// timeout case witness.
MaxTimeoutWitnessSize() lntypes.WeightUnit
MaxTimeoutWitnessSize() int
// TimeoutScript returns the redeem script required to unlock the htlc
// after timeout.
@ -436,7 +436,7 @@ func (h *HtlcScriptV2) SuccessScript() []byte {
}
// MaxSuccessWitnessSize returns maximum success witness size.
func (h *HtlcScriptV2) MaxSuccessWitnessSize() lntypes.WeightUnit {
func (h *HtlcScriptV2) MaxSuccessWitnessSize() int {
// Calculate maximum success witness size
//
// - number_of_witness_elements: 1 byte
@ -446,11 +446,11 @@ func (h *HtlcScriptV2) MaxSuccessWitnessSize() lntypes.WeightUnit {
// - preimage: 32 bytes
// - witness_script_length: 1 byte
// - witness_script: len(script) bytes
return lntypes.WeightUnit(1 + 1 + 73 + 1 + 32 + 1 + len(h.script))
return 1 + 1 + 73 + 1 + 32 + 1 + len(h.script)
}
// MaxTimeoutWitnessSize returns maximum timeout witness size.
func (h *HtlcScriptV2) MaxTimeoutWitnessSize() lntypes.WeightUnit {
func (h *HtlcScriptV2) MaxTimeoutWitnessSize() int {
// Calculate maximum timeout witness size
//
// - number_of_witness_elements: 1 byte
@ -461,7 +461,7 @@ func (h *HtlcScriptV2) MaxTimeoutWitnessSize() lntypes.WeightUnit {
// - zero: 1 byte
// - witness_script_length: 1 byte
// - witness_script: len(script) bytes
return lntypes.WeightUnit(1 + 1 + 73 + 1 + 33 + 1 + 1 + len(h.script))
return 1 + 1 + 73 + 1 + 33 + 1 + 1 + len(h.script)
}
// SuccessSequence returns the sequence to spend this htlc in the success case.
@ -735,7 +735,7 @@ func (h *HtlcScriptV3) SuccessScript() []byte {
// MaxSuccessWitnessSize returns the maximum witness size for the
// success case witness.
func (h *HtlcScriptV3) MaxSuccessWitnessSize() lntypes.WeightUnit {
func (h *HtlcScriptV3) MaxSuccessWitnessSize() int {
// Calculate maximum success witness size
//
// - number_of_witness_elements: 1 byte
@ -755,7 +755,7 @@ func (h *HtlcScriptV3) MaxSuccessWitnessSize() lntypes.WeightUnit {
// MaxTimeoutWitnessSize returns the maximum witness size for the
// timeout case witness.
func (h *HtlcScriptV3) MaxTimeoutWitnessSize() lntypes.WeightUnit {
func (h *HtlcScriptV3) MaxTimeoutWitnessSize() int {
// Calculate maximum timeout witness size
//
// - number_of_witness_elements: 1 byte
@ -768,7 +768,7 @@ func (h *HtlcScriptV3) MaxTimeoutWitnessSize() lntypes.WeightUnit {
// - leafVersionAndParity: 1
// - internalPubkey: 32
// - proof: 32
return lntypes.WeightUnit(1 + 1 + 64 + 1 + 36 + 1 + 65)
return 1 + 1 + 64 + 1 + 36 + 1 + 65
}
// SuccessSequence returns the sequence to spend this htlc in the

@ -15,7 +15,7 @@ import (
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/wire"
"github.com/lightninglabs/aperture/l402"
"github.com/lightninglabs/aperture/lsat"
"github.com/lightninglabs/loop/loopdb"
looprpc "github.com/lightninglabs/loop/swapserverrpc"
"github.com/lightningnetwork/lnd/lnrpc"
@ -161,14 +161,14 @@ func (s *grpcSwapServerClient) stop() {
var _ swapServerClient = (*grpcSwapServerClient)(nil)
func newSwapServerClient(cfg *ClientConfig, l402Store l402.Store) (
func newSwapServerClient(cfg *ClientConfig, lsatStore lsat.Store) (
*grpcSwapServerClient, error) {
// Create the server connection with the interceptor that will handle
// the L402 protocol for us.
clientInterceptor := l402.NewInterceptor(
cfg.Lnd, l402Store, serverRPCTimeout, cfg.MaxL402Cost,
cfg.MaxL402Fee, false,
// the LSAT protocol for us.
clientInterceptor := lsat.NewInterceptor(
cfg.Lnd, lsatStore, serverRPCTimeout, cfg.MaxLsatCost,
cfg.MaxLsatFee, false,
)
serverConn, err := getSwapServerConn(
cfg.ServerAddress, cfg.ProxyAddress, cfg.SwapServerNoTLS,
@ -636,7 +636,7 @@ const (
paymentTypeInvoice
)
// routeCancelMetadata contains cancellation information for swaps that are
// routeCancelMetadata contains cancelation information for swaps that are
// canceled because the client could not route off-chain to the server.
type routeCancelMetadata struct {
// paymentType is the type of payment that failed.
@ -895,7 +895,7 @@ func rpcRouteCancel(details *outCancelDetails) (
// proxyAddr indicates that a SOCKS proxy found at the address should be used to
// establish the connection.
func getSwapServerConn(address, proxyAddress string, insecure bool,
tlsPath string, interceptor *l402.ClientInterceptor) (*grpc.ClientConn,
tlsPath string, interceptor *lsat.ClientInterceptor) (*grpc.ClientConn,
error) {
// Create a dial options array.

@ -1,9 +1,9 @@
FROM golang:1.21.9-bookworm
FROM golang:1.16.3-buster
RUN apt-get update && apt-get install -y \
git \
protobuf-compiler='3.21.12*' \
clang-format='1:14.0*'
protobuf-compiler='3.6*' \
clang-format='1:7.0*'
# We don't want any default values for these variables to make sure they're
# explicitly provided by parsing the go.mod file. Otherwise we might forget to
@ -11,15 +11,11 @@ RUN apt-get update && apt-get install -y \
ARG PROTOBUF_VERSION
ENV PROTOC_GEN_GO_GRPC_VERSION="v1.1.0"
ENV GOCACHE=/tmp/build/.cache
ENV GOMODCACHE=/tmp/build/.modcache
RUN cd /tmp \
&& mkdir -p /tmp/build/.cache \
&& mkdir -p /tmp/build/.modcache \
&& go install google.golang.org/protobuf/cmd/protoc-gen-go@${PROTOBUF_VERSION} \
&& go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@${PROTOC_GEN_GO_GRPC_VERSION} \
&& chmod -R 777 /tmp/build/
&& export GO111MODULE=on \
&& go get google.golang.org/protobuf/cmd/protoc-gen-go@${PROTOBUF_VERSION} \
&& go get google.golang.org/grpc/cmd/protoc-gen-go-grpc@${PROTOC_GEN_GO_GRPC_VERSION}
WORKDIR /build

@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.30.0
// protoc v3.21.12
// protoc v3.6.1
// source: common.proto
// We can't change this to swapserverrpc, it would be a breaking change because
@ -36,8 +36,9 @@ type HopHint struct {
ChanId uint64 `protobuf:"varint,2,opt,name=chan_id,json=chanId,proto3" json:"chan_id,omitempty"`
// The base fee of the channel denominated in millisatoshis.
FeeBaseMsat uint32 `protobuf:"varint,3,opt,name=fee_base_msat,json=feeBaseMsat,proto3" json:"fee_base_msat,omitempty"`
// The fee rate of the channel for sending one satoshi across it denominated in
// millionths of a satoshi.
//
//The fee rate of the channel for sending one satoshi across it denominated in
//millionths of a satoshi.
FeeProportionalMillionths uint32 `protobuf:"varint,4,opt,name=fee_proportional_millionths,json=feeProportionalMillionths,proto3" json:"fee_proportional_millionths,omitempty"`
// The time-lock delta of the channel.
CltvExpiryDelta uint32 `protobuf:"varint,5,opt,name=cltv_expiry_delta,json=cltvExpiryDelta,proto3" json:"cltv_expiry_delta,omitempty"`
@ -115,8 +116,9 @@ type RouteHint struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// A list of hop hints that when chained together can assist in reaching a
// specific destination.
//
//A list of hop hints that when chained together can assist in reaching a
//specific destination.
HopHints []*HopHint `protobuf:"bytes,1,rep,name=hop_hints,json=hopHints,proto3" json:"hop_hints,omitempty"`
}

@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.30.0
// protoc v3.21.12
// protoc v3.6.1
// source: instantout.proto
// We can't change this to swapserverrpc, it would be a breaking change because

@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.30.0
// protoc v3.21.12
// protoc v3.6.1
// source: reservation.proto
// We can't change this to swapserverrpc, it would be a breaking change because

@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.30.0
// protoc v3.21.12
// protoc v3.6.1
// source: server.proto
// We can't change this to swapserverrpc, it would be a breaking change because
@ -25,26 +25,27 @@ const (
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// *
// This enum defines the protocol versions that clients may adhere to. Note that
// this is not a flagged enum. If a particular protocol version adds a feature,
// then in general all the preceding features are also supported. Exception to this
// is when features get deprecated.
//*
//This enum defines the protocol versions that clients may adhere to. Note that
//this is not a flagged enum. If a particular protocol version adds a feature,
//then in general all the preceding features are also supported. Exception to this
//is when features get deprecated.
type ProtocolVersion int32
const (
// / No protocol version reported at all.
/// No protocol version reported at all.
ProtocolVersion_LEGACY ProtocolVersion = 0
// / Client may attempt to send the loop out payment in multiple parts.
/// Client may attempt to send the loop out payment in multiple parts.
ProtocolVersion_MULTI_LOOP_OUT ProtocolVersion = 1
// *
// Loop will use native segwit (P2WSH) htlcs by default, while externally
// published htlcs may use native (P2WSH) or nested (NP2WSH) segwit as well.
//*
//Loop will use native segwit (P2WSH) htlcs by default, while externally
//published htlcs may use native (P2WSH) or nested (NP2WSH) segwit as well.
ProtocolVersion_NATIVE_SEGWIT_LOOP_IN ProtocolVersion = 2
// Once the on chain loop out htlc is confirmed, the client can push the swap
// preimage to the server to speed up claim of their off chain htlc (acquiring
// incoming liquidity more quickly than if the server waited for the on chain
// claim tx).
//
//Once the on chain loop out htlc is confirmed, the client can push the swap
//preimage to the server to speed up claim of their off chain htlc (acquiring
//incoming liquidity more quickly than if the server waited for the on chain
//claim tx).
ProtocolVersion_PREIMAGE_PUSH_LOOP_OUT ProtocolVersion = 3
// The client will propose a cltv expiry height for loop out.
ProtocolVersion_USER_EXPIRY_LOOP_OUT ProtocolVersion = 4
@ -137,20 +138,23 @@ const (
ServerSwapState_SERVER_HTLC_PUBLISHED ServerSwapState = 1
// The swap completed successfully.
ServerSwapState_SERVER_SUCCESS ServerSwapState = 2
// The swap failed for a reason that is unknown to the server, this is only
// set for older swaps.
//
//The swap failed for a reason that is unknown to the server, this is only
//set for older swaps.
ServerSwapState_SERVER_FAILED_UNKNOWN ServerSwapState = 3
// No htlc was confirmed in time for the loop in swap to complete.
ServerSwapState_SERVER_FAILED_NO_HTLC ServerSwapState = 4
// A loop in htlc confirmed on chain, but it did not have the correct value.
ServerSwapState_SERVER_FAILED_INVALID_HTLC_AMOUNT ServerSwapState = 5
// We did not succeed in completing the loop in off chain payment before the
// timeout.
//
//We did not succeed in completing the loop in off chain payment before the
//timeout.
ServerSwapState_SERVER_FAILED_OFF_CHAIN_TIMEOUT ServerSwapState = 6
// The on chain timeout was claimed.
ServerSwapState_SERVER_FAILED_TIMEOUT ServerSwapState = 7
// The server could not publish the loop out on chain htlc before the deadline
// provided.
//
//The server could not publish the loop out on chain htlc before the deadline
//provided.
ServerSwapState_SERVER_FAILED_SWAP_DEADLINE ServerSwapState = 8
// The server could not publish the loop out on chain htlc.
ServerSwapState_SERVER_FAILED_HTLC_PUBLICATION ServerSwapState = 9
@ -165,10 +169,12 @@ const (
// The client canceled the swap because they could not route the swap
// payment.
ServerSwapState_SERVER_CLIENT_INVOICE_CANCEL ServerSwapState = 14
// A loop in swap was rejected because it contained multiple outputs for a
// single swap.
//
//A loop in swap was rejected because it contained multiple outputs for a
//single swap.
ServerSwapState_SERVER_FAILED_MULTIPLE_SWAP_SCRIPTS ServerSwapState = 15
// The swap failed during creation.
//
//The swap failed during creation.
ServerSwapState_SERVER_FAILED_INITIALIZATION ServerSwapState = 16
)
@ -300,19 +306,25 @@ func (RoutePaymentType) EnumDescriptor() ([]byte, []int) {
type PaymentFailureReason int32
const (
// Payment isn't failed (yet).
//
//Payment isn't failed (yet).
PaymentFailureReason_LND_FAILURE_REASON_NONE PaymentFailureReason = 0
// There are more routes to try, but the payment timeout was exceeded.
//
//There are more routes to try, but the payment timeout was exceeded.
PaymentFailureReason_LND_FAILURE_REASON_TIMEOUT PaymentFailureReason = 1
// All possible routes were tried and failed permanently. Or were no
// routes to the destination at all.
//
//All possible routes were tried and failed permanently. Or were no
//routes to the destination at all.
PaymentFailureReason_LND_FAILURE_REASON_NO_ROUTE PaymentFailureReason = 2
// A non-recoverable error has occured.
//
//A non-recoverable error has occured.
PaymentFailureReason_LND_FAILURE_REASON_ERROR PaymentFailureReason = 3
// Payment details incorrect (unknown hash, invalid amt or
// invalid final cltv delta)
//
//Payment details incorrect (unknown hash, invalid amt or
//invalid final cltv delta)
PaymentFailureReason_LND_FAILURE_REASON_INCORRECT_PAYMENT_DETAILS PaymentFailureReason = 4
// Insufficient local balance.
//
//Insufficient local balance.
PaymentFailureReason_LND_FAILURE_REASON_INSUFFICIENT_BALANCE PaymentFailureReason = 5
)
@ -465,9 +477,9 @@ type ServerLoopOutRequest struct {
ReceiverKey []byte `protobuf:"bytes,1,opt,name=receiver_key,json=receiverKey,proto3" json:"receiver_key,omitempty"`
SwapHash []byte `protobuf:"bytes,2,opt,name=swap_hash,json=swapHash,proto3" json:"swap_hash,omitempty"`
Amt uint64 `protobuf:"varint,3,opt,name=amt,proto3" json:"amt,omitempty"`
// / The unix time in seconds we want the on-chain swap to be published by.
/// The unix time in seconds we want the on-chain swap to be published by.
SwapPublicationDeadline int64 `protobuf:"varint,4,opt,name=swap_publication_deadline,json=swapPublicationDeadline,proto3" json:"swap_publication_deadline,omitempty"`
// / The protocol version that the client adheres to.
/// The protocol version that the client adheres to.
ProtocolVersion ProtocolVersion `protobuf:"varint,5,opt,name=protocol_version,json=protocolVersion,proto3,enum=looprpc.ProtocolVersion" json:"protocol_version,omitempty"`
// The requested absolute block height of the on-chain htlc. This is
// subjected to min and max constraints as reported in the LoopOutTerms
@ -476,13 +488,10 @@ type ServerLoopOutRequest struct {
// The user agent string that identifies the software running on the user's
// side. This can be changed in the user's client software but it _SHOULD_
// conform to the following pattern:
//
// Agent-Name/semver-version(/additional-info)
//
// Agent-Name/semver-version(/additional-info)
// Examples:
//
// loopd/v0.10.0-beta/commit=3b635821
// litd/v0.2.0-alpha/commit=326d754
// loopd/v0.10.0-beta/commit=3b635821
// litd/v0.2.0-alpha/commit=326d754
UserAgent string `protobuf:"bytes,7,opt,name=user_agent,json=userAgent,proto3" json:"user_agent,omitempty"`
}
@ -657,11 +666,11 @@ type ServerLoopOutQuoteRequest struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// / The swap amount. If zero, a quote for a maximum amt swap will be given.
/// The swap amount. If zero, a quote for a maximum amt swap will be given.
Amt uint64 `protobuf:"varint,1,opt,name=amt,proto3" json:"amt,omitempty"`
// / The unix time in seconds we want the on-chain swap to be published by.
/// The unix time in seconds we want the on-chain swap to be published by.
SwapPublicationDeadline int64 `protobuf:"varint,2,opt,name=swap_publication_deadline,json=swapPublicationDeadline,proto3" json:"swap_publication_deadline,omitempty"`
// / The protocol version that the client adheres to.
/// The protocol version that the client adheres to.
ProtocolVersion ProtocolVersion `protobuf:"varint,3,opt,name=protocol_version,json=protocolVersion,proto3,enum=looprpc.ProtocolVersion" json:"protocol_version,omitempty"`
// The requested absolute block height of the on-chain htlc. This is
// subjected to min and max constraints as reported in the LoopOutTerms
@ -670,13 +679,10 @@ type ServerLoopOutQuoteRequest struct {
// The user agent string that identifies the software running on the user's
// side. This can be changed in the user's client software but it _SHOULD_
// conform to the following pattern:
//
// Agent-Name/semver-version(/additional-info)
//
// Agent-Name/semver-version(/additional-info)
// Examples:
//
// loopd/v0.10.0-beta/commit=3b635821
// litd/v0.2.0-alpha/commit=326d754
// loopd/v0.10.0-beta/commit=3b635821
// litd/v0.2.0-alpha/commit=326d754
UserAgent string `protobuf:"bytes,5,opt,name=user_agent,json=userAgent,proto3" json:"user_agent,omitempty"`
}
@ -753,9 +759,9 @@ type ServerLoopOutQuote struct {
unknownFields protoimpl.UnknownFields
SwapPaymentDest string `protobuf:"bytes,1,opt,name=swap_payment_dest,json=swapPaymentDest,proto3" json:"swap_payment_dest,omitempty"`
// / The total estimated swap fee given the quote amt.
/// The total estimated swap fee given the quote amt.
SwapFee int64 `protobuf:"varint,2,opt,name=swap_fee,json=swapFee,proto3" json:"swap_fee,omitempty"`
// / Deprecated, total swap fee given quote amt is calculated in swap_fee.
/// Deprecated, total swap fee given quote amt is calculated in swap_fee.
//
// Deprecated: Marked as deprecated in server.proto.
SwapFeeRate int64 `protobuf:"varint,3,opt,name=swap_fee_rate,json=swapFeeRate,proto3" json:"swap_fee_rate,omitempty"`
@ -861,18 +867,15 @@ type ServerLoopOutTermsRequest struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// / The protocol version that the client adheres to.
/// The protocol version that the client adheres to.
ProtocolVersion ProtocolVersion `protobuf:"varint,1,opt,name=protocol_version,json=protocolVersion,proto3,enum=looprpc.ProtocolVersion" json:"protocol_version,omitempty"`
// The user agent string that identifies the software running on the user's
// side. This can be changed in the user's client software but it _SHOULD_
// conform to the following pattern:
//
// Agent-Name/semver-version(/additional-info)
//
// Agent-Name/semver-version(/additional-info)
// Examples:
//
// loopd/v0.10.0-beta/commit=3b635821
// litd/v0.2.0-alpha/commit=326d754
// loopd/v0.10.0-beta/commit=3b635821
// litd/v0.2.0-alpha/commit=326d754
UserAgent string `protobuf:"bytes,2,opt,name=user_agent,json=userAgent,proto3" json:"user_agent,omitempty"`
}
@ -1006,20 +1009,17 @@ type ServerLoopInRequest struct {
Amt uint64 `protobuf:"varint,3,opt,name=amt,proto3" json:"amt,omitempty"`
SwapInvoice string `protobuf:"bytes,4,opt,name=swap_invoice,json=swapInvoice,proto3" json:"swap_invoice,omitempty"`
LastHop []byte `protobuf:"bytes,5,opt,name=last_hop,json=lastHop,proto3" json:"last_hop,omitempty"`
// / The protocol version that the client adheres to.
/// The protocol version that the client adheres to.
ProtocolVersion ProtocolVersion `protobuf:"varint,6,opt,name=protocol_version,json=protocolVersion,proto3,enum=looprpc.ProtocolVersion" json:"protocol_version,omitempty"`
// An invoice that can be used for the purpose of probing.
ProbeInvoice string `protobuf:"bytes,7,opt,name=probe_invoice,json=probeInvoice,proto3" json:"probe_invoice,omitempty"`
// The user agent string that identifies the software running on the user's
// side. This can be changed in the user's client software but it _SHOULD_
// conform to the following pattern:
//
// Agent-Name/semver-version(/additional-info)
//
// Agent-Name/semver-version(/additional-info)
// Examples:
//
// loopd/v0.10.0-beta/commit=3b635821
// litd/v0.2.0-alpha/commit=326d754
// loopd/v0.10.0-beta/commit=3b635821
// litd/v0.2.0-alpha/commit=326d754
UserAgent string `protobuf:"bytes,8,opt,name=user_agent,json=userAgent,proto3" json:"user_agent,omitempty"`
}
@ -1195,7 +1195,7 @@ type ServerLoopInQuoteRequest struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// / The swap amount. If zero, a quote for a maximum amt swap will be given.
/// The swap amount. If zero, a quote for a maximum amt swap will be given.
Amt uint64 `protobuf:"varint,1,opt,name=amt,proto3" json:"amt,omitempty"`
// The destination pubkey.
Pubkey []byte `protobuf:"bytes,3,opt,name=pubkey,proto3" json:"pubkey,omitempty"`
@ -1203,18 +1203,15 @@ type ServerLoopInQuoteRequest struct {
LastHop []byte `protobuf:"bytes,4,opt,name=last_hop,json=lastHop,proto3" json:"last_hop,omitempty"`
// Optional route hints to reach the destination through private channels.
RouteHints []*RouteHint `protobuf:"bytes,5,rep,name=route_hints,json=routeHints,proto3" json:"route_hints,omitempty"`
// / The protocol version that the client adheres to.
/// The protocol version that the client adheres to.
ProtocolVersion ProtocolVersion `protobuf:"varint,2,opt,name=protocol_version,json=protocolVersion,proto3,enum=looprpc.ProtocolVersion" json:"protocol_version,omitempty"`
// The user agent string that identifies the software running on the user's
// side. This can be changed in the user's client software but it _SHOULD_
// conform to the following pattern:
//
// Agent-Name/semver-version(/additional-info)
//
// Agent-Name/semver-version(/additional-info)
// Examples:
//
// loopd/v0.10.0-beta/commit=3b635821
// litd/v0.2.0-alpha/commit=326d754
// loopd/v0.10.0-beta/commit=3b635821
// litd/v0.2.0-alpha/commit=326d754
UserAgent string `protobuf:"bytes,6,opt,name=user_agent,json=userAgent,proto3" json:"user_agent,omitempty"`
}
@ -1382,18 +1379,15 @@ type ServerLoopInTermsRequest struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// / The protocol version that the client adheres to.
/// The protocol version that the client adheres to.
ProtocolVersion ProtocolVersion `protobuf:"varint,1,opt,name=protocol_version,json=protocolVersion,proto3,enum=looprpc.ProtocolVersion" json:"protocol_version,omitempty"`
// The user agent string that identifies the software running on the user's
// side. This can be changed in the user's client software but it _SHOULD_
// conform to the following pattern:
//
// Agent-Name/semver-version(/additional-info)
//
// Agent-Name/semver-version(/additional-info)
// Examples:
//
// loopd/v0.10.0-beta/commit=3b635821
// litd/v0.2.0-alpha/commit=326d754
// loopd/v0.10.0-beta/commit=3b635821
// litd/v0.2.0-alpha/commit=326d754
UserAgent string `protobuf:"bytes,2,opt,name=user_agent,json=userAgent,proto3" json:"user_agent,omitempty"`
}
@ -1508,8 +1502,9 @@ type ServerLoopOutPushPreimageRequest struct {
// The protocol version that the client adheres to.
ProtocolVersion ProtocolVersion `protobuf:"varint,1,opt,name=protocol_version,json=protocolVersion,proto3,enum=looprpc.ProtocolVersion" json:"protocol_version,omitempty"`
// Preimage is the preimage of the loop out swap that we wish to push to the
// server to speed up off-chain claim once the on-chain htlc has confirmed.
//
//Preimage is the preimage of the loop out swap that we wish to push to the
//server to speed up off-chain claim once the on-chain htlc has confirmed.
Preimage []byte `protobuf:"bytes,2,opt,name=preimage,proto3" json:"preimage,omitempty"`
}
@ -1898,7 +1893,6 @@ type CancelLoopOutSwapRequest struct {
// Additional information about the swap cancelation.
//
// Types that are assignable to CancelInfo:
//
// *CancelLoopOutSwapRequest_RouteCancel
CancelInfo isCancelLoopOutSwapRequest_CancelInfo `protobuf_oneof:"cancel_info"`
}

@ -217,5 +217,5 @@ func (s *Sweeper) GetSweepFee(ctx context.Context,
weight := weightEstimate.Weight()
return feeRate.FeeForWeight(weight), nil
return feeRate.FeeForWeight(int64(weight)), nil
}

@ -17,8 +17,7 @@ func init() {
UseLogger(build.NewSubLogger("SWEEP", nil))
}
// batchPrefixLogger returns a logger that prefixes all log messages with
// the ID.
// batchPrefixLogger returns a logger that prefixes all log messages with the ID.
func batchPrefixLogger(batchID string) btclog.Logger {
return build.NewPrefixLog(fmt.Sprintf("[Batch %s]", batchID), log)
}

@ -3,7 +3,6 @@ package sweepbatcher
import (
"context"
"database/sql"
"fmt"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg"
@ -21,20 +20,16 @@ type BaseDB interface {
// GetBatchSweeps fetches all the sweeps that are part a batch.
GetBatchSweeps(ctx context.Context, batchID int32) (
[]sqlc.Sweep, error)
// GetBatchSweptAmount returns the total amount of sats swept by a
// (confirmed) batch.
GetBatchSweptAmount(ctx context.Context, batchID int32) (int64, error)
[]sqlc.GetBatchSweepsRow, error)
// GetSweepStatus returns true if the sweep has been completed.
GetSweepStatus(ctx context.Context, swapHash []byte) (bool, error)
// GetParentBatch fetches the parent batch of a completed sweep.
GetParentBatch(ctx context.Context, swapHash []byte) (sqlc.SweepBatch,
error)
// GetSwapUpdates fetches all the updates for a swap.
GetSwapUpdates(ctx context.Context, swapHash []byte) (
[]sqlc.SwapUpdate, error)
// GetUnconfirmedBatches fetches all the batches from the
// FetchUnconfirmedSweepBatches fetches all the batches from the
// database that are not in a confirmed state.
GetUnconfirmedBatches(ctx context.Context) ([]sqlc.SweepBatch, error)
@ -43,9 +38,6 @@ type BaseDB interface {
InsertBatch(ctx context.Context, arg sqlc.InsertBatchParams) (
int32, error)
// DropBatch drops a batch from the database.
DropBatch(ctx context.Context, id int32) error
// UpdateBatch updates a batch in the database.
UpdateBatch(ctx context.Context, arg sqlc.UpdateBatchParams) error
@ -78,8 +70,8 @@ func NewSQLStore(db BaseDB, network *chaincfg.Params) *SQLStore {
// FetchUnconfirmedSweepBatches fetches all the batches from the database that
// are not in a confirmed state.
func (s *SQLStore) FetchUnconfirmedSweepBatches(ctx context.Context) (
[]*dbBatch, error) {
func (s *SQLStore) FetchUnconfirmedSweepBatches(ctx context.Context) ([]*dbBatch,
error) {
var batches []*dbBatch
@ -108,24 +100,6 @@ func (s *SQLStore) InsertSweepBatch(ctx context.Context, batch *dbBatch) (int32,
return s.baseDb.InsertBatch(ctx, batchToInsertArgs(*batch))
}
// DropBatch drops a batch from the database. Note that we only use this call
// for batches that have no sweeps and so we'd not be able to resume.
func (s *SQLStore) DropBatch(ctx context.Context, id int32) error {
readOpts := loopdb.NewSqlReadOpts()
return s.baseDb.ExecTx(ctx, readOpts, func(tx *sqlc.Queries) error {
dbSweeps, err := tx.GetBatchSweeps(ctx, id)
if err != nil {
return err
}
if len(dbSweeps) != 0 {
return fmt.Errorf("cannot drop a non-empty batch")
}
return tx.DropBatch(ctx, id)
})
}
// UpdateSweepBatch updates a batch in the database.
func (s *SQLStore) UpdateSweepBatch(ctx context.Context, batch *dbBatch) error {
return s.baseDb.UpdateBatch(ctx, batchToUpdateArgs(*batch))
@ -150,7 +124,14 @@ func (s *SQLStore) FetchBatchSweeps(ctx context.Context, id int32) (
}
for _, dbSweep := range dbSweeps {
sweep, err := s.convertSweepRow(dbSweep)
updates, err := s.baseDb.GetSwapUpdates(
ctx, dbSweep.SwapHash,
)
if err != nil {
return err
}
sweep, err := s.convertSweepRow(dbSweep, updates)
if err != nil {
return err
}
@ -167,34 +148,6 @@ func (s *SQLStore) FetchBatchSweeps(ctx context.Context, id int32) (
return sweeps, nil
}
// TotalSweptAmount returns the total amount swept by a (confirmed) batch.
func (s *SQLStore) TotalSweptAmount(ctx context.Context, id int32) (
btcutil.Amount, error) {
amt, err := s.baseDb.GetBatchSweptAmount(ctx, id)
if err != nil {
return 0, err
}
return btcutil.Amount(amt), nil
}
// GetParentBatch fetches the parent batch of a completed sweep.
func (s *SQLStore) GetParentBatch(ctx context.Context, swapHash lntypes.Hash) (
*dbBatch, error) {
batch, err := s.baseDb.GetParentBatch(ctx, swapHash[:])
if err != nil {
return nil, err
}
if err != nil {
return nil, err
}
return convertBatchRow(batch), nil
}
// UpsertSweep inserts a sweep into the database, or updates an existing sweep
// if it already exists.
func (s *SQLStore) UpsertSweep(ctx context.Context, sweep *dbSweep) error {
@ -249,6 +202,9 @@ type dbSweep struct {
// Completed indicates whether this sweep is completed.
Completed bool
// LoopOut is the loop out that the sweep belongs to.
LoopOut *loopdb.LoopOut
}
// convertBatchRow converts a batch row from db to a sweepbatcher.Batch struct.
@ -340,7 +296,9 @@ func batchToUpdateArgs(batch dbBatch) sqlc.UpdateBatchParams {
}
// convertSweepRow converts a sweep row from db to a sweep struct.
func (s *SQLStore) convertSweepRow(row sqlc.Sweep) (dbSweep, error) {
func (s *SQLStore) convertSweepRow(row sqlc.GetBatchSweepsRow,
updates []sqlc.SwapUpdate) (dbSweep, error) {
sweep := dbSweep{
ID: row.ID,
BatchID: row.BatchID,
@ -364,7 +322,40 @@ func (s *SQLStore) convertSweepRow(row sqlc.Sweep) (dbSweep, error) {
Index: uint32(row.OutpointIndex),
}
return sweep, nil
sweep.LoopOut, err = loopdb.ConvertLoopOutRow(
s.network,
sqlc.GetLoopOutSwapRow{
ID: row.ID,
SwapHash: row.SwapHash,
Preimage: row.Preimage,
InitiationTime: row.InitiationTime,
AmountRequested: row.AmountRequested,
CltvExpiry: row.CltvExpiry,
MaxMinerFee: row.MaxMinerFee,
MaxSwapFee: row.MaxSwapFee,
InitiationHeight: row.InitiationHeight,
ProtocolVersion: row.ProtocolVersion,
Label: row.Label,
DestAddress: row.DestAddress,
SwapInvoice: row.SwapInvoice,
MaxSwapRoutingFee: row.MaxSwapRoutingFee,
SweepConfTarget: row.SweepConfTarget,
HtlcConfirmations: row.HtlcConfirmations,
OutgoingChanSet: row.OutgoingChanSet,
PrepayInvoice: row.PrepayInvoice,
MaxPrepayRoutingFee: row.MaxPrepayRoutingFee,
PublicationDeadline: row.PublicationDeadline,
SingleSweep: row.SingleSweep,
SenderScriptPubkey: row.SenderScriptPubkey,
ReceiverScriptPubkey: row.ReceiverScriptPubkey,
SenderInternalPubkey: row.SenderInternalPubkey,
ReceiverInternalPubkey: row.ReceiverInternalPubkey,
ClientKeyFamily: row.ClientKeyFamily,
ClientKeyIndex: row.ClientKeyIndex,
}, updates,
)
return sweep, err
}
// sweepToUpsertArgs converts a Sweep struct to the arguments needed to insert.

@ -5,7 +5,6 @@ import (
"errors"
"sort"
"github.com/btcsuite/btcd/btcutil"
"github.com/lightningnetwork/lnd/lntypes"
)
@ -23,7 +22,7 @@ func NewStoreMock() *StoreMock {
}
}
// FetchUnconfirmedSweepBatches fetches all the loop out sweep batches from the
// FetchUnconfirmedBatches fetches all the loop out sweep batches from the
// database that are not in a confirmed state.
func (s *StoreMock) FetchUnconfirmedSweepBatches(ctx context.Context) (
[]*dbBatch, error) {
@ -56,12 +55,6 @@ func (s *StoreMock) InsertSweepBatch(ctx context.Context,
return id, nil
}
// DropBatch drops a batch from the database.
func (s *StoreMock) DropBatch(ctx context.Context, id int32) error {
delete(s.batches, id)
return nil
}
// UpdateSweepBatch updates a batch in the database.
func (s *StoreMock) UpdateSweepBatch(ctx context.Context,
batch *dbBatch) error {
@ -130,44 +123,3 @@ func (s *StoreMock) AssertSweepStored(id lntypes.Hash) bool {
_, ok := s.sweeps[id]
return ok
}
// GetParentBatch returns the parent batch of a swap.
func (s *StoreMock) GetParentBatch(ctx context.Context, swapHash lntypes.Hash) (
*dbBatch, error) {
for _, sweep := range s.sweeps {
if sweep.SwapHash == swapHash {
batch, ok := s.batches[sweep.BatchID]
if !ok {
return nil, errors.New("batch not found")
}
return &batch, nil
}
}
return nil, errors.New("batch not found")
}
// TotalSweptAmount returns the total amount of BTC that has been swept from a
// batch.
func (s *StoreMock) TotalSweptAmount(ctx context.Context, batchID int32) (
btcutil.Amount, error) {
batch, ok := s.batches[batchID]
if !ok {
return 0, errors.New("batch not found")
}
if batch.State != batchConfirmed && batch.State != batchClosed {
return 0, nil
}
var total btcutil.Amount
for _, sweep := range s.sweeps {
if sweep.BatchID == batchID {
total += sweep.Amount
}
}
return 0, nil
}

@ -3,8 +3,6 @@ package sweepbatcher
import (
"bytes"
"context"
"encoding/hex"
"errors"
"fmt"
"math"
"sync"
@ -35,6 +33,10 @@ const (
// fee rate is increased when an rbf is attempted.
defaultFeeRateStep = chainfee.SatPerKWeight(100)
// defaultBatchConfTarget is the default confirmation target of the
// batch transaction.
defaultBatchConfTarget = 12
// batchConfHeight is the default confirmation height of the batch
// transaction.
batchConfHeight = 3
@ -45,7 +47,7 @@ const (
)
var (
ErrBatchShuttingDown = errors.New("batch shutting down")
ErrBatchShuttingDown = fmt.Errorf("batch shutting down")
)
// sweep stores any data related to sweeping a specific outpoint.
@ -193,11 +195,7 @@ type batch struct {
// main event loop.
callLeave chan struct{}
// stopped signals that the batch has stopped.
stopped chan struct{}
// quit is owned by the parent batcher and signals that the batch must
// stop.
// quit signals that the batch must stop.
quit chan struct{}
// wallet is the wallet client used to create and publish the batch
@ -211,7 +209,7 @@ type batch struct {
// signerClient is the signer client used to sign the batch transaction.
signerClient lndclient.SignerClient
// muSig2SignSweep includes all the required functionality to collect
// muSig2Kit includes all the required functionality to collect
// and verify signatures by the swap server in order to cooperatively
// sweep funds.
muSig2SignSweep MuSig2SignSweep
@ -261,7 +259,6 @@ type batchKit struct {
purger Purger
store BatcherStore
log btclog.Logger
quit chan struct{}
}
// scheduleNextCall schedules the next call to the batch handler's main event
@ -271,9 +268,6 @@ func (b *batch) scheduleNextCall() (func(), error) {
case b.callEnter <- struct{}{}:
case <-b.quit:
return func() {}, ErrBatcherShuttingDown
case <-b.stopped:
return func() {}, ErrBatchShuttingDown
}
@ -297,8 +291,7 @@ func NewBatch(cfg batchConfig, bk batchKit) *batch {
errChan: make(chan error, 1),
callEnter: make(chan struct{}),
callLeave: make(chan struct{}),
stopped: make(chan struct{}),
quit: bk.quit,
quit: make(chan struct{}),
batchTxid: bk.batchTxid,
wallet: bk.wallet,
chainNotifier: bk.chainNotifier,
@ -312,22 +305,7 @@ func NewBatch(cfg batchConfig, bk batchKit) *batch {
}
// NewBatchFromDB creates a new batch that already existed in storage.
func NewBatchFromDB(cfg batchConfig, bk batchKit) (*batch, error) {
// Make sure the batch is not empty.
if len(bk.sweeps) == 0 {
// This should never happen, as this precondition is already
// ensured in spinUpBatchFromDB.
return nil, fmt.Errorf("empty batch is not allowed")
}
// Assign batchConfTarget to primary sweep's confTarget.
for _, sweep := range bk.sweeps {
if sweep.swapHash == bk.primaryID {
cfg.batchConfTarget = sweep.confTarget
break
}
}
func NewBatchFromDB(cfg batchConfig, bk batchKit) *batch {
return &batch{
id: bk.id,
state: bk.state,
@ -340,8 +318,7 @@ func NewBatchFromDB(cfg batchConfig, bk batchKit) (*batch, error) {
errChan: make(chan error, 1),
callEnter: make(chan struct{}),
callLeave: make(chan struct{}),
stopped: make(chan struct{}),
quit: bk.quit,
quit: make(chan struct{}),
batchTxid: bk.batchTxid,
batchPkScript: bk.batchPkScript,
rbfCache: bk.rbfCache,
@ -354,7 +331,7 @@ func NewBatchFromDB(cfg batchConfig, bk batchKit) (*batch, error) {
store: bk.store,
log: bk.log,
cfg: &cfg,
}, nil
}
}
// addSweep tries to add a sweep to the batch. If this is the first sweep being
@ -468,7 +445,7 @@ func (b *batch) Run(ctx context.Context) error {
runCtx, cancel := context.WithCancel(ctx)
defer func() {
cancel()
close(b.stopped)
close(b.quit)
b.wg.Wait()
}()
@ -561,12 +538,6 @@ func (b *batch) publish(ctx context.Context) error {
coopSuccess bool
)
if len(b.sweeps) == 0 {
b.log.Debugf("skipping publish: no sweeps in the batch")
return nil
}
// Run the RBF rate update.
err = b.updateRbfRate(ctx)
if err != nil {
@ -603,11 +574,9 @@ func (b *batch) publishBatch(ctx context.Context) (btcutil.Amount, error) {
batchTx.LockTime = uint32(b.currentHeight)
var (
batchAmt btcutil.Amount
prevOuts = make([]*wire.TxOut, 0, len(b.sweeps))
signDescs = make(
[]*lndclient.SignDescriptor, 0, len(b.sweeps),
)
batchAmt btcutil.Amount
prevOuts = make([]*wire.TxOut, 0, len(b.sweeps))
signDescs = make([]*lndclient.SignDescriptor, 0, len(b.sweeps))
sweeps = make([]sweep, 0, len(b.sweeps))
fee btcutil.Amount
inputCounter int
@ -696,7 +665,9 @@ func (b *batch) publishBatch(ctx context.Context) (btcutil.Amount, error) {
weightEstimate.AddP2TROutput()
fee = b.rbfCache.FeeRate.FeeForWeight(weightEstimate.Weight())
totalWeight := int64(weightEstimate.Weight())
fee = b.rbfCache.FeeRate.FeeForWeight(totalWeight)
// Clamp the calculated fee to the max allowed fee amount for the batch.
fee = clampBatchFee(fee, batchAmt)
@ -729,11 +700,9 @@ func (b *batch) publishBatch(ctx context.Context) (btcutil.Amount, error) {
batchTx.TxIn[i].Witness = witness
}
b.log.Infof("attempting to publish non-coop tx=%v with feerate=%v, "+
"totalfee=%v, sweeps=%d, destAddr=%s", batchTx.TxHash(),
b.rbfCache.FeeRate, fee, len(batchTx.TxIn), address)
b.debugLogTx("serialized non-coop sweep", batchTx)
b.log.Debugf("attempting to publish non-coop tx with feerate=%v, "+
"totalfee=%v, sweeps=%v, destAddr=%s", b.rbfCache.FeeRate, fee,
len(batchTx.TxIn), address.String())
err = b.wallet.PublishTransaction(
ctx, batchTx, labels.LoopOutBatchSweepSuccess(b.id),
@ -826,7 +795,9 @@ func (b *batch) publishBatchCoop(ctx context.Context) (btcutil.Amount,
weightEstimate.AddP2TROutput()
fee = b.rbfCache.FeeRate.FeeForWeight(weightEstimate.Weight())
totalWeight := int64(weightEstimate.Weight())
fee = b.rbfCache.FeeRate.FeeForWeight(totalWeight)
// Clamp the calculated fee to the max allowed fee amount for the batch.
fee = clampBatchFee(fee, batchAmt)
@ -875,11 +846,9 @@ func (b *batch) publishBatchCoop(ctx context.Context) (btcutil.Amount,
return fee, err, false
}
b.log.Infof("attempting to publish coop tx=%v with feerate=%v, "+
"totalfee=%v, sweeps=%d, destAddr=%s", batchTx.TxHash(),
b.rbfCache.FeeRate, fee, len(batchTx.TxIn), address)
b.debugLogTx("serialized coop sweep", batchTx)
b.log.Debugf("attempting to publish coop tx with feerate=%v, "+
"totalfee=%v, sweeps=%v, destAddr=%s", b.rbfCache.FeeRate, fee,
len(batchTx.TxIn), address.String())
err = b.wallet.PublishTransaction(
ctx, batchTx, labels.LoopOutBatchSweepSuccess(b.id),
@ -897,17 +866,6 @@ func (b *batch) publishBatchCoop(ctx context.Context) (btcutil.Amount,
return fee, nil, true
}
func (b *batch) debugLogTx(msg string, tx *wire.MsgTx) {
// Serialize the transaction and convert to hex string.
buf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize()))
if err := tx.Serialize(buf); err != nil {
b.log.Errorf("failed to serialize tx for debug log: %v", err)
return
}
b.log.Debugf("%s: %s", msg, hex.EncodeToString(buf.Bytes()))
}
// coopSignBatchTx collects the necessary signatures from the server in order
// to cooperatively sweep the funds.
func (b *batch) coopSignBatchTx(ctx context.Context, packet *psbt.Packet,
@ -1051,10 +1009,6 @@ func (b *batch) updateRbfRate(ctx context.Context) error {
// If the feeRate is unset then we never published before, so we
// retrieve the fee estimate from our wallet.
if b.rbfCache.FeeRate == 0 {
if b.cfg.batchConfTarget == 0 {
b.log.Warnf("updateRbfRate called with zero " +
"batchConfTarget")
}
b.log.Infof("initializing rbf fee rate for conf target=%v",
b.cfg.batchConfTarget)
rate, err := b.wallet.EstimateFeeRate(
@ -1182,32 +1136,6 @@ func (b *batch) monitorConfirmations(ctx context.Context) error {
return nil
}
// getFeePortionForSweep calculates the fee portion that each sweep should pay
// for the batch transaction. The fee is split evenly among the sweeps, If the
// fee cannot be split evenly, the remainder is paid by the first sweep.
func getFeePortionForSweep(spendTx *wire.MsgTx, numSweeps int,
totalSweptAmt btcutil.Amount) (btcutil.Amount, btcutil.Amount) {
totalFee := int64(totalSweptAmt) - spendTx.TxOut[0].Value
feePortionPerSweep := totalFee / int64(numSweeps)
roundingDiff := totalFee - (int64(numSweeps) * feePortionPerSweep)
return btcutil.Amount(feePortionPerSweep), btcutil.Amount(roundingDiff)
}
// getFeePortionPaidBySweep returns the fee portion that the sweep should pay
// for the batch transaction. If the sweep is the first sweep in the batch, it
// pays the rounding difference.
func getFeePortionPaidBySweep(spendTx *wire.MsgTx, feePortionPerSweep,
roundingDiff btcutil.Amount, sweep *sweep) btcutil.Amount {
if bytes.Equal(spendTx.TxIn[0].SignatureScript, sweep.htlc.SigScript) {
return feePortionPerSweep + roundingDiff
}
return feePortionPerSweep
}
// handleSpend handles a spend notification.
func (b *batch) handleSpend(ctx context.Context, spendTx *wire.MsgTx) error {
var (
@ -1223,14 +1151,12 @@ func (b *batch) handleSpend(ctx context.Context, spendTx *wire.MsgTx) error {
// sweeps that did not make it to the confirmed transaction and feed
// them back to the batcher. This will ensure that the sweeps will enter
// a new batch instead of remaining dangling.
var totalSweptAmt btcutil.Amount
for _, sweep := range b.sweeps {
found := false
for _, txIn := range spendTx.TxIn {
if txIn.PreviousOutPoint == sweep.outpoint {
found = true
totalSweptAmt += sweep.value
notifyList = append(notifyList, sweep)
}
}
@ -1250,13 +1176,7 @@ func (b *batch) handleSpend(ctx context.Context, spendTx *wire.MsgTx) error {
}
}
// Calculate the fee portion that each sweep should pay for the batch.
feePortionPaidPerSweep, roundingDifference := getFeePortionForSweep(
spendTx, len(notifyList), totalSweptAmt,
)
for _, sweep := range notifyList {
sweep := sweep
// Save the sweep as completed.
err := b.persistSweep(ctx, sweep, true)
if err != nil {
@ -1272,17 +1192,9 @@ func (b *batch) handleSpend(ctx context.Context, spendTx *wire.MsgTx) error {
continue
}
spendDetail := SpendDetail{
Tx: spendTx,
OnChainFeePortion: getFeePortionPaidBySweep(
spendTx, feePortionPaidPerSweep,
roundingDifference, &sweep,
),
}
// Dispatch the sweep notifier, we don't care about the outcome
// of this action so we don't wait for it.
go sweep.notifySweepSpend(ctx, &spendDetail)
go notifySweepSpend(ctx, sweep, spendTx)
}
// Proceed with purging the sweeps. This will feed the sweeps that
@ -1406,12 +1318,10 @@ func (b *batch) insertAndAcquireID(ctx context.Context) (int32, error) {
}
// notifySweepSpend writes the spendTx to the sweep's notifier channel.
func (s *sweep) notifySweepSpend(ctx context.Context,
spendDetail *SpendDetail) {
func notifySweepSpend(ctx context.Context, s sweep, spendTx *wire.MsgTx) {
select {
// Try to write the update to the notification channel.
case s.notifier.SpendChan <- spendDetail:
case s.notifier.SpendChan <- spendTx:
// If a quit signal was provided by the swap, continue.
case <-s.notifier.QuitChan:

@ -2,7 +2,6 @@ package sweepbatcher
import (
"context"
"errors"
"fmt"
"sync"
"time"
@ -13,9 +12,7 @@ import (
"github.com/btcsuite/btcd/wire"
"github.com/lightninglabs/lndclient"
"github.com/lightninglabs/loop/loopdb"
"github.com/lightninglabs/loop/swap"
"github.com/lightninglabs/loop/utils"
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
)
@ -49,83 +46,32 @@ const (
type BatcherStore interface {
// FetchUnconfirmedSweepBatches fetches all the batches from the
// database that are not in a confirmed state.
FetchUnconfirmedSweepBatches(ctx context.Context) ([]*dbBatch, error)
FetchUnconfirmedSweepBatches(ctx context.Context) ([]*dbBatch,
error)
// InsertSweepBatch inserts a batch into the database, returning the id
// of the inserted batch.
InsertSweepBatch(ctx context.Context, batch *dbBatch) (int32, error)
// DropBatch drops a batch from the database. This should only be used
// when a batch is empty.
DropBatch(ctx context.Context, id int32) error
InsertSweepBatch(ctx context.Context,
batch *dbBatch) (int32, error)
// UpdateSweepBatch updates a batch in the database.
UpdateSweepBatch(ctx context.Context, batch *dbBatch) error
UpdateSweepBatch(ctx context.Context,
batch *dbBatch) error
// ConfirmBatch confirms a batch by setting its state to confirmed.
ConfirmBatch(ctx context.Context, id int32) error
// FetchBatchSweeps fetches all the sweeps that belong to a batch.
FetchBatchSweeps(ctx context.Context, id int32) ([]*dbSweep, error)
FetchBatchSweeps(ctx context.Context,
id int32) ([]*dbSweep, error)
// UpsertSweep inserts a sweep into the database, or updates an existing
// sweep if it already exists.
UpsertSweep(ctx context.Context, sweep *dbSweep) error
// GetSweepStatus returns the completed status of the sweep.
GetSweepStatus(ctx context.Context, swapHash lntypes.Hash) (bool, error)
// GetParentBatch returns the parent batch of a (completed) sweep.
GetParentBatch(ctx context.Context, swapHash lntypes.Hash) (*dbBatch,
error)
// TotalSweptAmount returns the total amount swept by a (confirmed)
// batch.
TotalSweptAmount(ctx context.Context, id int32) (btcutil.Amount, error)
}
// SweepInfo stores any data related to sweeping a specific outpoint.
type SweepInfo struct {
// ConfTarget is the confirmation target of the sweep.
ConfTarget int32
// Timeout is the timeout of the swap that the sweep belongs to.
Timeout int32
// InitiationHeight is the height at which the swap was initiated.
InitiationHeight int32
// HTLC is the HTLC that is being swept.
HTLC swap.Htlc
// Preimage is the preimage of the HTLC that is being swept.
Preimage lntypes.Preimage
// SwapInvoicePaymentAddr is the payment address of the swap invoice.
SwapInvoicePaymentAddr [32]byte
// HTLCKeys is the set of keys used to sign the HTLC.
HTLCKeys loopdb.HtlcKeys
// HTLCSuccessEstimator is a function that estimates the weight of the
// HTLC success script.
HTLCSuccessEstimator func(*input.TxWeightEstimator) error
// ProtocolVersion is the protocol version of the swap that the sweep
// belongs to.
ProtocolVersion loopdb.ProtocolVersion
// IsExternalAddr is true if the sweep spends to a non-wallet address.
IsExternalAddr bool
// DestAddr is the destination address of the sweep.
DestAddr btcutil.Address
}
// SweepFetcher is used to get details of a sweep.
type SweepFetcher interface {
// FetchSweep returns details of the sweep with the given hash.
FetchSweep(ctx context.Context, hash lntypes.Hash) (*SweepInfo, error)
GetSweepStatus(ctx context.Context, swapHash lntypes.Hash) (
bool, error)
}
// MuSig2SignSweep is a function that can be used to sign a sweep transaction
@ -156,22 +102,11 @@ type SweepRequest struct {
Notifier *SpendNotifier
}
type SpendDetail struct {
// Tx is the transaction that spent the outpoint.
Tx *wire.MsgTx
// OnChainFeePortion is the fee portion that was paid to get this sweep
// confirmed on chain. This is the difference between the value of the
// outpoint and the value of all sweeps that were included in the batch
// divided by the number of sweeps.
OnChainFeePortion btcutil.Amount
}
// SpendNotifier is a notifier that is used to notify the requester of a sweep
// that the sweep was successful.
type SpendNotifier struct {
// SpendChan is a channel where the spend details are received.
SpendChan chan *SpendDetail
SpendChan chan *wire.MsgTx
// SpendErrChan is a channel where spend errors are received.
SpendErrChan chan error
@ -181,7 +116,7 @@ type SpendNotifier struct {
}
var (
ErrBatcherShuttingDown = errors.New("batcher shutting down")
ErrBatcherShuttingDown = fmt.Errorf("batcher shutting down")
)
// Batcher is a system that is responsible for accepting sweep requests and
@ -199,10 +134,6 @@ type Batcher struct {
// quit signals that the batch must stop.
quit chan struct{}
// initDone is a channel that is closed when the batcher has been
// initialized.
initDone chan struct{}
// wallet is the wallet kit client that is used by batches.
wallet lndclient.WalletKitClient
@ -229,8 +160,9 @@ type Batcher struct {
// batcher and the batches.
store BatcherStore
// sweepStore is used to load sweeps from the database.
sweepStore SweepFetcher
// swapStore includes all the database interactions that are needed for
// interacting with swaps.
swapStore loopdb.SwapStore
// wg is a waitgroup that is used to wait for all the goroutines to
// exit.
@ -242,14 +174,13 @@ func NewBatcher(wallet lndclient.WalletKitClient,
chainNotifier lndclient.ChainNotifierClient,
signerClient lndclient.SignerClient, musig2ServerSigner MuSig2SignSweep,
verifySchnorrSig VerifySchnorrSig, chainparams *chaincfg.Params,
store BatcherStore, sweepStore SweepFetcher) *Batcher {
store BatcherStore, swapStore loopdb.SwapStore) *Batcher {
return &Batcher{
batches: make(map[int32]*batch),
sweepReqs: make(chan SweepRequest),
errChan: make(chan error, 1),
quit: make(chan struct{}),
initDone: make(chan struct{}),
wallet: wallet,
chainNotifier: chainNotifier,
signerClient: signerClient,
@ -257,7 +188,7 @@ func NewBatcher(wallet lndclient.WalletKitClient,
VerifySchnorrSig: verifySchnorrSig,
chainParams: chainparams,
store: store,
sweepStore: sweepStore,
swapStore: swapStore,
}
}
@ -266,7 +197,6 @@ func (b *Batcher) Run(ctx context.Context) error {
runCtx, cancel := context.WithCancel(ctx)
defer func() {
cancel()
close(b.quit)
for _, batch := range b.batches {
batch.Wait()
@ -289,9 +219,6 @@ func (b *Batcher) Run(ctx context.Context) error {
}
}
// Signal that the batcher has been initialized.
close(b.initDone)
for {
select {
case sweepReq := <-b.sweepReqs:
@ -343,7 +270,8 @@ func (b *Batcher) handleSweep(ctx context.Context, sweep *sweep,
// can't attach its notifier to the batch as that is no longer running.
// Instead we directly detect and return the spend here.
if completed && *notifier != (SpendNotifier{}) {
return b.monitorSpendAndNotify(ctx, sweep, notifier)
go b.monitorSpendAndNotify(ctx, sweep, notifier)
return nil
}
sweep.notifier = notifier
@ -360,25 +288,22 @@ func (b *Batcher) handleSweep(ctx context.Context, sweep *sweep,
if batch.sweepExists(sweep.swapHash) {
accepted, err := batch.addSweep(ctx, sweep)
if err != nil && !errors.Is(err, ErrBatchShuttingDown) {
if err != nil {
return err
}
if !accepted {
return fmt.Errorf("existing sweep %x was not "+
"accepted by batch %d",
sweep.swapHash[:6], batch.id)
"accepted by batch %d", sweep.swapHash[:6],
batch.id)
}
// The sweep was updated in the batch, our job is done.
return nil
}
}
// If one of the batches accepts the sweep, we provide it to that batch.
for _, batch := range b.batches {
accepted, err := batch.addSweep(ctx, sweep)
if err != nil && !errors.Is(err, ErrBatchShuttingDown) {
if err != nil && err != ErrBatchShuttingDown {
return err
}
@ -416,6 +341,7 @@ func (b *Batcher) handleSweep(ctx context.Context, sweep *sweep,
func (b *Batcher) spinUpBatch(ctx context.Context) (*batch, error) {
cfg := batchConfig{
maxTimeoutDistance: defaultMaxTimeoutDistance,
batchConfTarget: defaultBatchConfTarget,
}
switch b.chainParams {
@ -435,7 +361,6 @@ func (b *Batcher) spinUpBatch(ctx context.Context) (*batch, error) {
verifySchnorrSig: b.VerifySchnorrSig,
purger: b.AddSweep,
store: b.store,
quit: b.quit,
}
batch := NewBatch(cfg, batchKit)
@ -464,23 +389,23 @@ func (b *Batcher) spinUpBatch(ctx context.Context) (*batch, error) {
// spinUpBatchDB spins up a batch that already existed in storage, then
// returns it.
func (b *Batcher) spinUpBatchFromDB(ctx context.Context, batch *batch) error {
cfg := batchConfig{
maxTimeoutDistance: batch.cfg.maxTimeoutDistance,
batchConfTarget: defaultBatchConfTarget,
}
rbfCache := rbfCache{
LastHeight: batch.rbfCache.LastHeight,
FeeRate: batch.rbfCache.FeeRate,
}
dbSweeps, err := b.store.FetchBatchSweeps(ctx, batch.id)
if err != nil {
return err
}
if len(dbSweeps) == 0 {
log.Infof("skipping restored batch %d as it has no sweeps",
batch.id)
// It is safe to drop this empty batch as it has no sweeps.
err := b.store.DropBatch(ctx, batch.id)
if err != nil {
log.Warnf("unable to drop empty batch %d: %v",
batch.id, err)
}
return nil
return fmt.Errorf("batch %d has no sweeps", batch.id)
}
primarySweep := dbSweeps[0]
@ -488,7 +413,7 @@ func (b *Batcher) spinUpBatchFromDB(ctx context.Context, batch *batch) error {
sweeps := make(map[lntypes.Hash]sweep)
for _, dbSweep := range dbSweeps {
sweep, err := b.convertSweep(ctx, dbSweep)
sweep, err := b.convertSweep(dbSweep)
if err != nil {
return err
}
@ -496,13 +421,6 @@ func (b *Batcher) spinUpBatchFromDB(ctx context.Context, batch *batch) error {
sweeps[sweep.swapHash] = *sweep
}
rbfCache := rbfCache{
LastHeight: batch.rbfCache.LastHeight,
FeeRate: batch.rbfCache.FeeRate,
}
logger := batchPrefixLogger(fmt.Sprintf("%d", batch.id))
batchKit := batchKit{
id: batch.id,
batchTxid: batch.batchTxid,
@ -519,18 +437,10 @@ func (b *Batcher) spinUpBatchFromDB(ctx context.Context, batch *batch) error {
verifySchnorrSig: b.VerifySchnorrSig,
purger: b.AddSweep,
store: b.store,
log: logger,
quit: b.quit,
}
cfg := batchConfig{
maxTimeoutDistance: batch.cfg.maxTimeoutDistance,
log: batchPrefixLogger(fmt.Sprintf("%d", batch.id)),
}
newBatch, err := NewBatchFromDB(cfg, batchKit)
if err != nil {
return fmt.Errorf("failed in NewBatchFromDB: %w", err)
}
newBatch := NewBatchFromDB(cfg, batchKit)
// We add the batch to our map of batches and start it.
b.batches[batch.id] = newBatch
@ -599,88 +509,57 @@ func (b *Batcher) FetchUnconfirmedBatches(ctx context.Context) ([]*batch,
// monitorSpendAndNotify monitors the spend of a specific outpoint and writes
// the response back to the response channel.
func (b *Batcher) monitorSpendAndNotify(ctx context.Context, sweep *sweep,
notifier *SpendNotifier) error {
notifier *SpendNotifier) {
b.wg.Add(1)
defer b.wg.Done()
spendCtx, cancel := context.WithCancel(ctx)
defer cancel()
// First get the batch that completed the sweep.
parentBatch, err := b.store.GetParentBatch(ctx, sweep.swapHash)
if err != nil {
return err
}
// Then we get the total amount that was swept by the batch.
totalSwept, err := b.store.TotalSweptAmount(ctx, parentBatch.ID)
if err != nil {
return err
}
spendChan, spendErr, err := b.chainNotifier.RegisterSpendNtfn(
spendCtx, &sweep.outpoint, sweep.htlc.PkScript,
sweep.initiationHeight,
)
if err != nil {
return err
select {
case notifier.SpendErrChan <- err:
case <-ctx.Done():
}
_ = b.writeToErrChan(ctx, err)
return
}
b.wg.Add(1)
go func() {
defer b.wg.Done()
log.Infof("Batcher monitoring spend for swap %x",
sweep.swapHash[:6])
log.Infof("Batcher monitoring spend for swap %x", sweep.swapHash[:6])
for {
for {
select {
case spend := <-spendChan:
select {
case spend := <-spendChan:
spendTx := spend.SpendingTx
// Calculate the fee portion that each sweep
// should pay for the batch.
feePortionPerSweep, roundingDifference :=
getFeePortionForSweep(
spendTx, len(spendTx.TxIn),
totalSwept,
)
onChainFeePortion := getFeePortionPaidBySweep(
spendTx, feePortionPerSweep,
roundingDifference, sweep,
)
// Notify the requester of the spend
// with the spend details, including the fee
// portion for this particular sweep.
spendDetail := &SpendDetail{
Tx: spendTx,
OnChainFeePortion: onChainFeePortion,
}
select {
case notifier.SpendChan <- spendDetail:
case <-ctx.Done():
}
return
case err := <-spendErr:
select {
case notifier.SpendErrChan <- err:
case <-ctx.Done():
}
_ = b.writeToErrChan(ctx, err)
return
case <-notifier.QuitChan:
return
case notifier.SpendChan <- spend.SpendingTx:
case <-ctx.Done():
}
return
case err := <-spendErr:
select {
case notifier.SpendErrChan <- err:
case <-ctx.Done():
return
}
}
}()
return nil
_ = b.writeToErrChan(ctx, err)
return
case <-notifier.QuitChan:
return
case <-ctx.Done():
return
}
}
}
func (b *Batcher) writeToErrChan(ctx context.Context, err error) error {
@ -694,128 +573,85 @@ func (b *Batcher) writeToErrChan(ctx context.Context, err error) error {
}
// convertSweep converts a fetched sweep from the database to a sweep that is
// ready to be processed by the batcher. It loads swap from loopdb by calling
// method FetchLoopOutSwap.
func (b *Batcher) convertSweep(ctx context.Context, dbSweep *dbSweep) (
*sweep, error) {
// ready to be processed by the batcher.
func (b *Batcher) convertSweep(dbSweep *dbSweep) (*sweep, error) {
swap := dbSweep.LoopOut
s, err := b.sweepStore.FetchSweep(ctx, dbSweep.SwapHash)
htlc, err := utils.GetHtlc(
dbSweep.SwapHash, &swap.Contract.SwapContract, b.chainParams,
)
if err != nil {
return nil, fmt.Errorf("failed to fetch sweep data for %x: %v",
dbSweep.SwapHash[:6], err)
return nil, err
}
swapPaymentAddr, err := utils.ObtainSwapPaymentAddr(
swap.Contract.SwapInvoice, b.chainParams,
)
if err != nil {
return nil, err
}
return &sweep{
swapHash: dbSweep.SwapHash,
swapHash: swap.Hash,
outpoint: dbSweep.Outpoint,
value: dbSweep.Amount,
confTarget: s.ConfTarget,
timeout: s.Timeout,
initiationHeight: s.InitiationHeight,
htlc: s.HTLC,
preimage: s.Preimage,
swapInvoicePaymentAddr: s.SwapInvoicePaymentAddr,
htlcKeys: s.HTLCKeys,
htlcSuccessEstimator: s.HTLCSuccessEstimator,
protocolVersion: s.ProtocolVersion,
isExternalAddr: s.IsExternalAddr,
destAddr: s.DestAddr,
confTarget: swap.Contract.SweepConfTarget,
timeout: swap.Contract.CltvExpiry,
initiationHeight: swap.Contract.InitiationHeight,
htlc: *htlc,
preimage: swap.Contract.Preimage,
swapInvoicePaymentAddr: *swapPaymentAddr,
htlcKeys: swap.Contract.HtlcKeys,
htlcSuccessEstimator: htlc.AddSuccessToEstimator,
protocolVersion: swap.Contract.ProtocolVersion,
isExternalAddr: swap.Contract.IsExternalAddr,
destAddr: swap.Contract.DestAddr,
}, nil
}
// LoopOutFetcher is used to load LoopOut swaps from the database.
// It is implemented by loopdb.SwapStore.
type LoopOutFetcher interface {
// FetchLoopOutSwap returns the loop out swap with the given hash.
FetchLoopOutSwap(ctx context.Context,
hash lntypes.Hash) (*loopdb.LoopOut, error)
}
// SwapStoreWrapper is LoopOutFetcher wrapper providing SweepFetcher interface.
type SwapStoreWrapper struct {
// swapStore is used to load LoopOut swaps from the database.
swapStore LoopOutFetcher
// chainParams are the chain parameters of the chain that is used by
// batches.
chainParams *chaincfg.Params
}
// fetchSweep fetches the sweep related information from the database.
func (b *Batcher) fetchSweep(ctx context.Context,
sweepReq SweepRequest) (*sweep, error) {
// FetchSweep returns details of the sweep with the given hash.
// Implements SweepFetcher interface.
func (f *SwapStoreWrapper) FetchSweep(ctx context.Context,
swapHash lntypes.Hash) (*SweepInfo, error) {
swapHash, err := lntypes.MakeHash(sweepReq.SwapHash[:])
if err != nil {
return nil, fmt.Errorf("failed to parse swapHash: %v", err)
}
swap, err := f.swapStore.FetchLoopOutSwap(ctx, swapHash)
swap, err := b.swapStore.FetchLoopOutSwap(ctx, swapHash)
if err != nil {
return nil, fmt.Errorf("failed to fetch loop out for %x: %v",
swapHash[:6], err)
}
htlc, err := utils.GetHtlc(
swapHash, &swap.Contract.SwapContract, f.chainParams,
swapHash, &swap.Contract.SwapContract, b.chainParams,
)
if err != nil {
return nil, fmt.Errorf("failed to get htlc: %v", err)
}
swapPaymentAddr, err := utils.ObtainSwapPaymentAddr(
swap.Contract.SwapInvoice, f.chainParams,
swap.Contract.SwapInvoice, b.chainParams,
)
if err != nil {
return nil, fmt.Errorf("failed to get payment addr: %v", err)
}
return &SweepInfo{
ConfTarget: swap.Contract.SweepConfTarget,
Timeout: swap.Contract.CltvExpiry,
InitiationHeight: swap.Contract.InitiationHeight,
HTLC: *htlc,
Preimage: swap.Contract.Preimage,
SwapInvoicePaymentAddr: *swapPaymentAddr,
HTLCKeys: swap.Contract.HtlcKeys,
HTLCSuccessEstimator: htlc.AddSuccessToEstimator,
ProtocolVersion: swap.Contract.ProtocolVersion,
IsExternalAddr: swap.Contract.IsExternalAddr,
DestAddr: swap.Contract.DestAddr,
}, nil
}
// NewSweepFetcherFromSwapStore accepts swapStore (e.g. loopdb) and returns
// a wrapper implementing SweepFetcher interface (suitable for NewBatcher).
func NewSweepFetcherFromSwapStore(swapStore LoopOutFetcher,
chainParams *chaincfg.Params) (*SwapStoreWrapper, error) {
return &SwapStoreWrapper{
swapStore: swapStore,
chainParams: chainParams,
}, nil
}
// fetchSweep fetches the sweep related information from the database.
func (b *Batcher) fetchSweep(ctx context.Context,
sweepReq SweepRequest) (*sweep, error) {
s, err := b.sweepStore.FetchSweep(ctx, sweepReq.SwapHash)
if err != nil {
return nil, fmt.Errorf("failed to fetch sweep data for %x: %v",
sweepReq.SwapHash[:6], err)
}
return &sweep{
swapHash: sweepReq.SwapHash,
swapHash: swap.Hash,
outpoint: sweepReq.Outpoint,
value: sweepReq.Value,
confTarget: s.ConfTarget,
timeout: s.Timeout,
initiationHeight: s.InitiationHeight,
htlc: s.HTLC,
preimage: s.Preimage,
swapInvoicePaymentAddr: s.SwapInvoicePaymentAddr,
htlcKeys: s.HTLCKeys,
htlcSuccessEstimator: s.HTLCSuccessEstimator,
protocolVersion: s.ProtocolVersion,
isExternalAddr: s.IsExternalAddr,
destAddr: s.DestAddr,
confTarget: swap.Contract.SweepConfTarget,
timeout: swap.Contract.CltvExpiry,
initiationHeight: swap.Contract.InitiationHeight,
htlc: *htlc,
preimage: swap.Contract.Preimage,
swapInvoicePaymentAddr: *swapPaymentAddr,
htlcKeys: swap.Contract.HtlcKeys,
htlcSuccessEstimator: htlc.AddSuccessToEstimator,
protocolVersion: swap.Contract.ProtocolVersion,
isExternalAddr: swap.Contract.IsExternalAddr,
destAddr: swap.Contract.DestAddr,
}, nil
}

@ -2,8 +2,7 @@ package sweepbatcher
import (
"context"
"errors"
"sync"
"strings"
"testing"
"time"
@ -12,7 +11,6 @@ import (
"github.com/btcsuite/btcd/wire"
"github.com/lightninglabs/loop/loopdb"
"github.com/lightninglabs/loop/test"
"github.com/lightninglabs/loop/utils"
"github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/stretchr/testify/require"
@ -40,20 +38,11 @@ func testMuSig2SignSweep(ctx context.Context,
}
var dummyNotifier = SpendNotifier{
SpendChan: make(chan *SpendDetail, ntfnBufferSize),
SpendChan: make(chan *wire.MsgTx, ntfnBufferSize),
SpendErrChan: make(chan error, ntfnBufferSize),
QuitChan: make(chan bool, ntfnBufferSize),
}
func checkBatcherError(t *testing.T, err error) {
if !errors.Is(err, context.Canceled) &&
!errors.Is(err, ErrBatcherShuttingDown) &&
!errors.Is(err, ErrBatchShuttingDown) {
require.NoError(t, err)
}
}
// TestSweepBatcherBatchCreation tests that sweep requests enter the expected
// batch based on their timeout distance.
func TestSweepBatcherBatchCreation(t *testing.T) {
@ -64,17 +53,16 @@ func TestSweepBatcherBatchCreation(t *testing.T) {
defer cancel()
store := loopdb.NewStoreMock(t)
sweepStore, err := NewSweepFetcherFromSwapStore(store, lnd.ChainParams)
require.NoError(t, err)
batcherStore := NewStoreMock()
batcher := NewBatcher(lnd.WalletKit, lnd.ChainNotifier, lnd.Signer,
testMuSig2SignSweep, nil, lnd.ChainParams, batcherStore,
sweepStore)
testMuSig2SignSweep, nil, lnd.ChainParams, batcherStore, store)
go func() {
err := batcher.Run(ctx)
checkBatcherError(t, err)
if !strings.Contains(err.Error(), "context canceled") {
require.NoError(t, err)
}
}()
// Create a sweep request.
@ -97,19 +85,19 @@ func TestSweepBatcherBatchCreation(t *testing.T) {
SwapInvoice: swapInvoice,
}
err = store.CreateLoopOut(ctx, sweepReq1.SwapHash, swap1)
err := store.CreateLoopOut(ctx, sweepReq1.SwapHash, swap1)
require.NoError(t, err)
store.AssertLoopOutStored()
// Deliver sweep request to batcher.
require.NoError(t, batcher.AddSweep(&sweepReq1))
batcher.sweepReqs <- sweepReq1
// Since a batch was created we check that it registered for its primary
// sweep's spend.
<-lnd.RegisterSpendChannel
// Insert the same swap twice, this should be a noop.
require.NoError(t, batcher.AddSweep(&sweepReq1))
batcher.sweepReqs <- sweepReq1
// Once batcher receives sweep request it will eventually spin up a
// batch.
@ -141,7 +129,7 @@ func TestSweepBatcherBatchCreation(t *testing.T) {
require.NoError(t, err)
store.AssertLoopOutStored()
require.NoError(t, batcher.AddSweep(&sweepReq2))
batcher.sweepReqs <- sweepReq2
// Batcher should not create a second batch as timeout distance is small
// enough.
@ -173,7 +161,7 @@ func TestSweepBatcherBatchCreation(t *testing.T) {
require.NoError(t, err)
store.AssertLoopOutStored()
require.NoError(t, batcher.AddSweep(&sweepReq3))
batcher.sweepReqs <- sweepReq3
// Batcher should create a second batch as timeout distance is greater
// than the threshold
@ -186,8 +174,7 @@ func TestSweepBatcherBatchCreation(t *testing.T) {
<-lnd.RegisterSpendChannel
require.Eventually(t, func() bool {
// Verify that each batch has the correct number of sweeps
// in it.
// Verify that each batch has the correct number of sweeps in it.
for _, batch := range batcher.batches {
switch batch.primarySweepID {
case sweepReq1.SwapHash:
@ -221,17 +208,16 @@ func TestSweepBatcherSimpleLifecycle(t *testing.T) {
defer cancel()
store := loopdb.NewStoreMock(t)
sweepStore, err := NewSweepFetcherFromSwapStore(store, lnd.ChainParams)
require.NoError(t, err)
batcherStore := NewStoreMock()
batcher := NewBatcher(lnd.WalletKit, lnd.ChainNotifier, lnd.Signer,
testMuSig2SignSweep, nil, lnd.ChainParams, batcherStore,
sweepStore)
testMuSig2SignSweep, nil, lnd.ChainParams, batcherStore, store)
go func() {
err := batcher.Run(ctx)
checkBatcherError(t, err)
if !strings.Contains(err.Error(), "context canceled") {
require.NoError(t, err)
}
}()
// Create a sweep request.
@ -254,12 +240,12 @@ func TestSweepBatcherSimpleLifecycle(t *testing.T) {
SweepConfTarget: 111,
}
err = store.CreateLoopOut(ctx, sweepReq1.SwapHash, swap1)
err := store.CreateLoopOut(ctx, sweepReq1.SwapHash, swap1)
require.NoError(t, err)
store.AssertLoopOutStored()
// Deliver sweep request to batcher.
require.NoError(t, batcher.AddSweep(&sweepReq1))
batcher.sweepReqs <- sweepReq1
// Eventually request will be consumed and a new batch will spin up.
require.Eventually(t, func() bool {
@ -361,17 +347,16 @@ func TestSweepBatcherSweepReentry(t *testing.T) {
defer cancel()
store := loopdb.NewStoreMock(t)
sweepStore, err := NewSweepFetcherFromSwapStore(store, lnd.ChainParams)
require.NoError(t, err)
batcherStore := NewStoreMock()
batcher := NewBatcher(lnd.WalletKit, lnd.ChainNotifier, lnd.Signer,
testMuSig2SignSweep, nil, lnd.ChainParams, batcherStore,
sweepStore)
testMuSig2SignSweep, nil, lnd.ChainParams, batcherStore, store)
go func() {
err := batcher.Run(ctx)
checkBatcherError(t, err)
if !strings.Contains(err.Error(), "context canceled") {
require.NoError(t, err)
}
}()
// Create some sweep requests with timeouts not too far away, in order
@ -395,7 +380,7 @@ func TestSweepBatcherSweepReentry(t *testing.T) {
SweepConfTarget: 111,
}
err = store.CreateLoopOut(ctx, sweepReq1.SwapHash, swap1)
err := store.CreateLoopOut(ctx, sweepReq1.SwapHash, swap1)
require.NoError(t, err)
store.AssertLoopOutStored()
@ -446,15 +431,15 @@ func TestSweepBatcherSweepReentry(t *testing.T) {
store.AssertLoopOutStored()
// Feed the sweeps to the batcher.
require.NoError(t, batcher.AddSweep(&sweepReq1))
batcher.sweepReqs <- sweepReq1
// After inserting the primary (first) sweep, a spend monitor should be
// registered.
<-lnd.RegisterSpendChannel
require.NoError(t, batcher.AddSweep(&sweepReq2))
batcher.sweepReqs <- sweepReq2
require.NoError(t, batcher.AddSweep(&sweepReq3))
batcher.sweepReqs <- sweepReq3
// Batcher should create a batch for the sweeps.
require.Eventually(t, func() bool {
@ -492,9 +477,7 @@ func TestSweepBatcherSweepReentry(t *testing.T) {
},
TxOut: []*wire.TxOut{
{
Value: int64(sweepReq1.Value.ToUnit(
btcutil.AmountSatoshi,
)),
Value: int64(sweepReq1.Value.ToUnit(btcutil.AmountSatoshi)),
PkScript: []byte{3, 2, 1},
},
},
@ -571,17 +554,16 @@ func TestSweepBatcherNonWalletAddr(t *testing.T) {
defer cancel()
store := loopdb.NewStoreMock(t)
sweepStore, err := NewSweepFetcherFromSwapStore(store, lnd.ChainParams)
require.NoError(t, err)
batcherStore := NewStoreMock()
batcher := NewBatcher(lnd.WalletKit, lnd.ChainNotifier, lnd.Signer,
testMuSig2SignSweep, nil, lnd.ChainParams, batcherStore,
sweepStore)
testMuSig2SignSweep, nil, lnd.ChainParams, batcherStore, store)
go func() {
err := batcher.Run(ctx)
checkBatcherError(t, err)
if !strings.Contains(err.Error(), "context canceled") {
require.NoError(t, err)
}
}()
// Create a sweep request.
@ -604,12 +586,12 @@ func TestSweepBatcherNonWalletAddr(t *testing.T) {
SwapInvoice: swapInvoice,
}
err = store.CreateLoopOut(ctx, sweepReq1.SwapHash, swap1)
err := store.CreateLoopOut(ctx, sweepReq1.SwapHash, swap1)
require.NoError(t, err)
store.AssertLoopOutStored()
// Deliver sweep request to batcher.
require.NoError(t, batcher.AddSweep(&sweepReq1))
batcher.sweepReqs <- sweepReq1
// Once batcher receives sweep request it will eventually spin up a
// batch.
@ -622,7 +604,7 @@ func TestSweepBatcherNonWalletAddr(t *testing.T) {
<-lnd.RegisterSpendChannel
// Insert the same swap twice, this should be a noop.
require.NoError(t, batcher.AddSweep(&sweepReq1))
batcher.sweepReqs <- sweepReq1
// Create a second sweep request that has a timeout distance less than
// our configured threshold.
@ -649,7 +631,7 @@ func TestSweepBatcherNonWalletAddr(t *testing.T) {
require.NoError(t, err)
store.AssertLoopOutStored()
require.NoError(t, batcher.AddSweep(&sweepReq2))
batcher.sweepReqs <- sweepReq2
// Batcher should create a second batch as first batch is a non wallet
// addr batch.
@ -686,7 +668,7 @@ func TestSweepBatcherNonWalletAddr(t *testing.T) {
require.NoError(t, err)
store.AssertLoopOutStored()
require.NoError(t, batcher.AddSweep(&sweepReq3))
batcher.sweepReqs <- sweepReq3
// Batcher should create a new batch as timeout distance is greater than
// the threshold
@ -699,8 +681,7 @@ func TestSweepBatcherNonWalletAddr(t *testing.T) {
<-lnd.RegisterSpendChannel
require.Eventually(t, func() bool {
// Verify that each batch has the correct number of sweeps
// in it.
// Verify that each batch has the correct number of sweeps in it.
for _, batch := range batcher.batches {
switch batch.primarySweepID {
case sweepReq1.SwapHash:
@ -739,17 +720,16 @@ func TestSweepBatcherComposite(t *testing.T) {
defer cancel()
store := loopdb.NewStoreMock(t)
sweepStore, err := NewSweepFetcherFromSwapStore(store, lnd.ChainParams)
require.NoError(t, err)
batcherStore := NewStoreMock()
batcher := NewBatcher(lnd.WalletKit, lnd.ChainNotifier, lnd.Signer,
testMuSig2SignSweep, nil, lnd.ChainParams, batcherStore,
sweepStore)
testMuSig2SignSweep, nil, lnd.ChainParams, batcherStore, store)
go func() {
err := batcher.Run(ctx)
checkBatcherError(t, err)
if !strings.Contains(err.Error(), "context canceled") {
require.NoError(t, err)
}
}()
// Create a sweep request.
@ -772,7 +752,7 @@ func TestSweepBatcherComposite(t *testing.T) {
SwapInvoice: swapInvoice,
}
err = store.CreateLoopOut(ctx, sweepReq1.SwapHash, swap1)
err := store.CreateLoopOut(ctx, sweepReq1.SwapHash, swap1)
require.NoError(t, err)
store.AssertLoopOutStored()
@ -899,7 +879,7 @@ func TestSweepBatcherComposite(t *testing.T) {
store.AssertLoopOutStored()
// Deliver sweep request to batcher.
require.NoError(t, batcher.AddSweep(&sweepReq1))
batcher.sweepReqs <- sweepReq1
// Once batcher receives sweep request it will eventually spin up a
// batch.
@ -912,9 +892,9 @@ func TestSweepBatcherComposite(t *testing.T) {
<-lnd.RegisterSpendChannel
// Insert the same swap twice, this should be a noop.
require.NoError(t, batcher.AddSweep(&sweepReq1))
batcher.sweepReqs <- sweepReq1
require.NoError(t, batcher.AddSweep(&sweepReq2))
batcher.sweepReqs <- sweepReq2
// Batcher should not create a second batch as timeout distance is small
// enough.
@ -922,7 +902,7 @@ func TestSweepBatcherComposite(t *testing.T) {
return len(batcher.batches) == 1
}, test.Timeout, eventuallyCheckFrequency)
require.NoError(t, batcher.AddSweep(&sweepReq3))
batcher.sweepReqs <- sweepReq3
// Batcher should create a second batch as this sweep pays to a non
// wallet address.
@ -934,7 +914,7 @@ func TestSweepBatcherComposite(t *testing.T) {
// sweep's spend.
<-lnd.RegisterSpendChannel
require.NoError(t, batcher.AddSweep(&sweepReq4))
batcher.sweepReqs <- sweepReq4
// Batcher should create a third batch as timeout distance is greater
// than the threshold.
@ -946,7 +926,7 @@ func TestSweepBatcherComposite(t *testing.T) {
// sweep's spend.
<-lnd.RegisterSpendChannel
require.NoError(t, batcher.AddSweep(&sweepReq5))
batcher.sweepReqs <- sweepReq5
// Batcher should not create a fourth batch as timeout distance is small
// enough for it to join the last batch.
@ -954,7 +934,7 @@ func TestSweepBatcherComposite(t *testing.T) {
return len(batcher.batches) == 3
}, test.Timeout, eventuallyCheckFrequency)
require.NoError(t, batcher.AddSweep(&sweepReq6))
batcher.sweepReqs <- sweepReq6
// Batcher should create a fourth batch as this sweep pays to a non
// wallet address.
@ -1004,537 +984,3 @@ func TestSweepBatcherComposite(t *testing.T) {
require.True(t, batcherStore.AssertSweepStored(sweepReq5.SwapHash))
require.True(t, batcherStore.AssertSweepStored(sweepReq6.SwapHash))
}
// makeTestTx creates a test transaction with a single output of the given
// value.
func makeTestTx(value int64) *wire.MsgTx {
tx := wire.NewMsgTx(wire.TxVersion)
tx.AddTxOut(wire.NewTxOut(value, nil))
return tx
}
// TestGetFeePortionForSweep tests that the fee portion for a sweep is correctly
// calculated.
func TestGetFeePortionForSweep(t *testing.T) {
tests := []struct {
name string
spendTxValue int64
numSweeps int
totalSweptAmt btcutil.Amount
expectedFeePortion btcutil.Amount
expectedRoundingDiff btcutil.Amount
}{
{
"Even Split",
100, 5, 200, 20, 0,
},
{
"Single Sweep",
100, 1, 200, 100, 0,
},
{
"With Rounding Diff",
200, 4, 350, 37, 2,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
spendTx := makeTestTx(tt.spendTxValue)
feePortion, roundingDiff := getFeePortionForSweep(
spendTx, tt.numSweeps, tt.totalSweptAmt,
)
require.Equal(t, tt.expectedFeePortion, feePortion)
require.Equal(t, tt.expectedRoundingDiff, roundingDiff)
})
}
}
// TestRestoringEmptyBatch tests that the batcher can be restored with an empty
// batch.
func TestRestoringEmptyBatch(t *testing.T) {
defer test.Guard(t)()
lnd := test.NewMockLnd()
ctx, cancel := context.WithCancel(context.Background())
store := loopdb.NewStoreMock(t)
sweepStore, err := NewSweepFetcherFromSwapStore(store, lnd.ChainParams)
require.NoError(t, err)
batcherStore := NewStoreMock()
_, err = batcherStore.InsertSweepBatch(ctx, &dbBatch{})
require.NoError(t, err)
batcher := NewBatcher(lnd.WalletKit, lnd.ChainNotifier, lnd.Signer,
testMuSig2SignSweep, nil, lnd.ChainParams, batcherStore,
sweepStore)
var wg sync.WaitGroup
wg.Add(1)
var runErr error
go func() {
defer wg.Done()
runErr = batcher.Run(ctx)
}()
// Wait for the batcher to be initialized.
<-batcher.initDone
// Create a sweep request.
sweepReq := SweepRequest{
SwapHash: lntypes.Hash{1, 1, 1},
Value: 111,
Outpoint: wire.OutPoint{
Hash: chainhash.Hash{1, 1},
Index: 1,
},
Notifier: &dummyNotifier,
}
swap := &loopdb.LoopOutContract{
SwapContract: loopdb.SwapContract{
CltvExpiry: 111,
AmountRequested: 111,
},
SwapInvoice: swapInvoice,
}
err = store.CreateLoopOut(ctx, sweepReq.SwapHash, swap)
require.NoError(t, err)
store.AssertLoopOutStored()
// Deliver sweep request to batcher.
require.NoError(t, batcher.AddSweep(&sweepReq))
// Since a batch was created we check that it registered for its primary
// sweep's spend.
<-lnd.RegisterSpendChannel
// Once batcher receives sweep request it will eventually spin up a
// batch.
require.Eventually(t, func() bool {
// Make sure that the sweep was stored and we have exactly one
// active batch.
return batcherStore.AssertSweepStored(sweepReq.SwapHash) &&
len(batcher.batches) == 1
}, test.Timeout, eventuallyCheckFrequency)
// Make sure we have only one batch stored (as we dropped the dormant
// one).
batches, err := batcherStore.FetchUnconfirmedSweepBatches(ctx)
require.NoError(t, err)
require.Len(t, batches, 1)
// Now make the batcher quit by canceling the context.
cancel()
wg.Wait()
checkBatcherError(t, runErr)
}
type loopStoreMock struct {
loops map[lntypes.Hash]*loopdb.LoopOut
mu sync.Mutex
}
func newLoopStoreMock() *loopStoreMock {
return &loopStoreMock{
loops: make(map[lntypes.Hash]*loopdb.LoopOut),
}
}
func (s *loopStoreMock) FetchLoopOutSwap(ctx context.Context,
hash lntypes.Hash) (*loopdb.LoopOut, error) {
s.mu.Lock()
defer s.mu.Unlock()
out, has := s.loops[hash]
if !has {
return nil, errors.New("loop not found")
}
return out, nil
}
func (s *loopStoreMock) putLoopOutSwap(hash lntypes.Hash, out *loopdb.LoopOut) {
s.mu.Lock()
defer s.mu.Unlock()
s.loops[hash] = out
}
// TestHandleSweepTwice tests that handing the same sweep twice must not
// add it to different batches.
func TestHandleSweepTwice(t *testing.T) {
defer test.Guard(t)()
lnd := test.NewMockLnd()
ctx, cancel := context.WithCancel(context.Background())
store := newLoopStoreMock()
sweepStore, err := NewSweepFetcherFromSwapStore(store, lnd.ChainParams)
require.NoError(t, err)
batcherStore := NewStoreMock()
batcher := NewBatcher(lnd.WalletKit, lnd.ChainNotifier, lnd.Signer,
testMuSig2SignSweep, nil, lnd.ChainParams, batcherStore,
sweepStore)
var wg sync.WaitGroup
wg.Add(1)
var runErr error
go func() {
defer wg.Done()
runErr = batcher.Run(ctx)
}()
// Wait for the batcher to be initialized.
<-batcher.initDone
const shortCltv = 111
const longCltv = 111 + defaultMaxTimeoutDistance + 6
// Create two sweep requests with CltvExpiry distant from each other
// to go assigned to separate batches.
sweepReq1 := SweepRequest{
SwapHash: lntypes.Hash{1, 1, 1},
Value: 111,
Outpoint: wire.OutPoint{
Hash: chainhash.Hash{1, 1},
Index: 1,
},
Notifier: &dummyNotifier,
}
loopOut1 := &loopdb.LoopOut{
Loop: loopdb.Loop{
Hash: lntypes.Hash{1, 1, 1},
},
Contract: &loopdb.LoopOutContract{
SwapContract: loopdb.SwapContract{
CltvExpiry: shortCltv,
AmountRequested: 111,
},
SwapInvoice: swapInvoice,
},
}
sweepReq2 := SweepRequest{
SwapHash: lntypes.Hash{2, 2, 2},
Value: 222,
Outpoint: wire.OutPoint{
Hash: chainhash.Hash{2, 2},
Index: 2,
},
Notifier: &dummyNotifier,
}
loopOut2 := &loopdb.LoopOut{
Loop: loopdb.Loop{
Hash: lntypes.Hash{2, 2, 2},
},
Contract: &loopdb.LoopOutContract{
SwapContract: loopdb.SwapContract{
CltvExpiry: longCltv,
AmountRequested: 222,
},
SwapInvoice: swapInvoice,
},
}
store.putLoopOutSwap(sweepReq1.SwapHash, loopOut1)
store.putLoopOutSwap(sweepReq2.SwapHash, loopOut2)
// Deliver sweep request to batcher.
require.NoError(t, batcher.AddSweep(&sweepReq1))
// Since two batches were created we check that it registered for its
// primary sweep's spend.
<-lnd.RegisterSpendChannel
// Deliver the second sweep. It will go to a separate batch,
// since CltvExpiry values are distant enough.
require.NoError(t, batcher.AddSweep(&sweepReq2))
<-lnd.RegisterSpendChannel
// Once batcher receives sweep request it will eventually spin up
// batches.
require.Eventually(t, func() bool {
// Make sure that the sweep was stored and we have exactly one
// active batch.
return batcherStore.AssertSweepStored(sweepReq1.SwapHash) &&
batcherStore.AssertSweepStored(sweepReq2.SwapHash) &&
len(batcher.batches) == 2
}, test.Timeout, eventuallyCheckFrequency)
// Change the second sweep so that it can be added to the first batch.
// Change CltvExpiry.
loopOut2 = &loopdb.LoopOut{
Loop: loopdb.Loop{
Hash: lntypes.Hash{2, 2, 2},
},
Contract: &loopdb.LoopOutContract{
SwapContract: loopdb.SwapContract{
CltvExpiry: shortCltv,
AmountRequested: 222,
},
SwapInvoice: swapInvoice,
},
}
store.putLoopOutSwap(sweepReq2.SwapHash, loopOut2)
// Re-add the second sweep. It is expected to stay in second batch,
// not added to both batches.
require.NoError(t, batcher.AddSweep(&sweepReq2))
require.Eventually(t, func() bool {
// Make sure there are two batches.
batches := batcher.batches
if len(batches) != 2 {
return false
}
// Make sure the second batch has the second sweep.
sweep2, has := batches[1].sweeps[sweepReq2.SwapHash]
if !has {
return false
}
// Make sure the second sweep's timeout has been updated.
if sweep2.timeout != shortCltv {
return false
}
return true
}, test.Timeout, eventuallyCheckFrequency)
// Make sure each batch has one sweep. If the second sweep was added to
// both batches, the following check won't pass.
require.Equal(t, 1, len(batcher.batches[0].sweeps))
require.Equal(t, 1, len(batcher.batches[1].sweeps))
// Now make the batcher quit by canceling the context.
cancel()
wg.Wait()
checkBatcherError(t, runErr)
}
// TestRestoringPreservesConfTarget tests that after the batch is written to DB
// and loaded back, its batchConfTarget value is preserved.
func TestRestoringPreservesConfTarget(t *testing.T) {
defer test.Guard(t)()
lnd := test.NewMockLnd()
ctx, cancel := context.WithCancel(context.Background())
store := loopdb.NewStoreMock(t)
sweepStore, err := NewSweepFetcherFromSwapStore(store, lnd.ChainParams)
require.NoError(t, err)
batcherStore := NewStoreMock()
batcher := NewBatcher(lnd.WalletKit, lnd.ChainNotifier, lnd.Signer,
testMuSig2SignSweep, nil, lnd.ChainParams, batcherStore,
sweepStore)
var wg sync.WaitGroup
wg.Add(1)
var runErr error
go func() {
defer wg.Done()
runErr = batcher.Run(ctx)
}()
// Wait for the batcher to be initialized.
<-batcher.initDone
// Create a sweep request.
sweepReq := SweepRequest{
SwapHash: lntypes.Hash{1, 1, 1},
Value: 111,
Outpoint: wire.OutPoint{
Hash: chainhash.Hash{1, 1},
Index: 1,
},
Notifier: &dummyNotifier,
}
swap := &loopdb.LoopOutContract{
SwapContract: loopdb.SwapContract{
CltvExpiry: 111,
AmountRequested: 111,
},
SwapInvoice: swapInvoice,
SweepConfTarget: 123,
}
err = store.CreateLoopOut(ctx, sweepReq.SwapHash, swap)
require.NoError(t, err)
store.AssertLoopOutStored()
// Deliver sweep request to batcher.
require.NoError(t, batcher.AddSweep(&sweepReq))
// Since a batch was created we check that it registered for its primary
// sweep's spend.
<-lnd.RegisterSpendChannel
// Once batcher receives sweep request it will eventually spin up a
// batch.
require.Eventually(t, func() bool {
// Make sure that the sweep was stored and we have exactly one
// active batch, with one sweep and proper batchConfTarget.
return batcherStore.AssertSweepStored(sweepReq.SwapHash) &&
len(batcher.batches) == 1 &&
len(batcher.batches[0].sweeps) == 1 &&
batcher.batches[0].cfg.batchConfTarget == 123
}, test.Timeout, eventuallyCheckFrequency)
// Make sure we have stored the batch.
batches, err := batcherStore.FetchUnconfirmedSweepBatches(ctx)
require.NoError(t, err)
require.Len(t, batches, 1)
// Now make the batcher quit by canceling the context.
cancel()
wg.Wait()
// Make sure the batcher exited without an error.
checkBatcherError(t, runErr)
// Now launch it again.
batcher = NewBatcher(lnd.WalletKit, lnd.ChainNotifier, lnd.Signer,
testMuSig2SignSweep, nil, lnd.ChainParams, batcherStore,
sweepStore)
ctx, cancel = context.WithCancel(context.Background())
wg.Add(1)
go func() {
defer wg.Done()
runErr = batcher.Run(ctx)
}()
// Wait for the batcher to be initialized.
<-batcher.initDone
// Wait for batch to load.
require.Eventually(t, func() bool {
return batcherStore.AssertSweepStored(sweepReq.SwapHash) &&
len(batcher.batches) == 1 &&
len(batcher.batches[0].sweeps) == 1
}, test.Timeout, eventuallyCheckFrequency)
// Make sure batchConfTarget was preserved.
require.Equal(t, 123, int(batcher.batches[0].cfg.batchConfTarget))
// Expect registration for spend notification.
<-lnd.RegisterSpendChannel
// Now make the batcher quit by canceling the context.
cancel()
wg.Wait()
// Make sure the batcher exited without an error.
checkBatcherError(t, runErr)
}
type sweepFetcherMock struct {
store map[lntypes.Hash]*SweepInfo
}
func (f *sweepFetcherMock) FetchSweep(ctx context.Context, hash lntypes.Hash) (
*SweepInfo, error) {
return f.store[hash], nil
}
// TestSweepFetcher tests providing custom sweep fetcher to Batcher.
func TestSweepFetcher(t *testing.T) {
defer test.Guard(t)()
lnd := test.NewMockLnd()
ctx, cancel := context.WithCancel(context.Background())
// Extract payment address from the invoice.
swapPaymentAddr, err := utils.ObtainSwapPaymentAddr(
swapInvoice, lnd.ChainParams,
)
require.NoError(t, err)
sweepFetcher := &sweepFetcherMock{
store: map[lntypes.Hash]*SweepInfo{
{1, 1, 1}: {
ConfTarget: 123,
Timeout: 111,
SwapInvoicePaymentAddr: *swapPaymentAddr,
},
},
}
batcherStore := NewStoreMock()
batcher := NewBatcher(lnd.WalletKit, lnd.ChainNotifier, lnd.Signer,
testMuSig2SignSweep, nil, lnd.ChainParams, batcherStore,
sweepFetcher)
var wg sync.WaitGroup
wg.Add(1)
var runErr error
go func() {
defer wg.Done()
runErr = batcher.Run(ctx)
}()
// Wait for the batcher to be initialized.
<-batcher.initDone
// Create a sweep request.
sweepReq := SweepRequest{
SwapHash: lntypes.Hash{1, 1, 1},
Value: 111,
Outpoint: wire.OutPoint{
Hash: chainhash.Hash{1, 1},
Index: 1,
},
Notifier: &dummyNotifier,
}
// Deliver sweep request to batcher.
require.NoError(t, batcher.AddSweep(&sweepReq))
// Since a batch was created we check that it registered for its primary
// sweep's spend.
<-lnd.RegisterSpendChannel
// Once batcher receives sweep request it will eventually spin up a
// batch.
require.Eventually(t, func() bool {
// Make sure that the sweep was stored and we have exactly one
// active batch, with one sweep and proper batchConfTarget.
return batcherStore.AssertSweepStored(sweepReq.SwapHash) &&
len(batcher.batches) == 1 &&
len(batcher.batches[0].sweeps) == 1 &&
batcher.batches[0].cfg.batchConfTarget == 123
}, test.Timeout, eventuallyCheckFrequency)
// Make sure we have stored the batch.
batches, err := batcherStore.FetchUnconfirmedSweepBatches(ctx)
require.NoError(t, err)
require.Len(t, batches, 1)
// Now make the batcher quit by canceling the context.
cancel()
wg.Wait()
// Make sure the batcher exited without an error.
checkBatcherError(t, runErr)
}

@ -278,24 +278,11 @@ func (h *mockLightningClient) ListInvoices(_ context.Context,
// ListPayments makes a paginated call to our list payments endpoint.
func (h *mockLightningClient) ListPayments(_ context.Context,
req lndclient.ListPaymentsRequest) (*lndclient.ListPaymentsResponse,
_ lndclient.ListPaymentsRequest) (*lndclient.ListPaymentsResponse,
error) {
if req.Offset >= uint64(len(h.lnd.Payments)) {
return &lndclient.ListPaymentsResponse{}, nil
}
lastIndexOffset := req.Offset + req.MaxPayments
if lastIndexOffset > uint64(len(h.lnd.Payments)) {
lastIndexOffset = uint64(len(h.lnd.Payments))
}
result := h.lnd.Payments[req.Offset:lastIndexOffset]
return &lndclient.ListPaymentsResponse{
Payments: result,
FirstIndexOffset: req.Offset,
LastIndexOffset: lastIndexOffset - 1,
Payments: h.lnd.Payments,
}, nil
}

@ -70,7 +70,7 @@ func mockMuSig2SignSweep(ctx context.Context,
return nil, nil, nil
}
func newSwapClient(t *testing.T, config *clientConfig) *Client {
func newSwapClient(config *clientConfig) *Client {
sweeper := &sweep.Sweeper{
Lnd: config.LndServices,
}
@ -79,16 +79,11 @@ func newSwapClient(t *testing.T, config *clientConfig) *Client {
batcherStore := sweepbatcher.NewStoreMock()
sweepStore, err := sweepbatcher.NewSweepFetcherFromSwapStore(
config.Store, config.LndServices.ChainParams,
)
require.NoError(t, err)
batcher := sweepbatcher.NewBatcher(
config.LndServices.WalletKit, config.LndServices.ChainNotifier,
config.LndServices.Signer, mockMuSig2SignSweep,
mockVerifySchnorrSigSuccess, config.LndServices.ChainParams,
batcherStore, sweepStore,
batcherStore, config.Store,
)
executor := newExecutor(&executorConfig{
@ -133,7 +128,7 @@ func createClientTestContext(t *testing.T,
return expiryChan
}
swapClient := newSwapClient(t, &clientConfig{
swapClient := newSwapClient(&clientConfig{
LndServices: &clientLnd.LndServices,
Server: serverMock,
Store: store,

@ -1,4 +1,4 @@
FROM golang:1.22
FROM golang:1.21
RUN apt-get update && apt-get install -y git
ENV GOCACHE=/tmp/build/.cache

@ -12,7 +12,7 @@ import (
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
"github.com/lightninglabs/lndclient"
"github.com/lightningnetwork/lnd/channeldb/models"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing/route"
"github.com/lightningnetwork/lnd/zpay32"
@ -30,40 +30,41 @@ var (
// isPublicNode checks if a node is public, by simply checking if there's any
// channels reported to the node.
func isPublicNode(ctx context.Context, lndClient lndclient.LightningClient,
func isPublicNode(ctx context.Context, lnd *lndclient.LndServices,
pubKey [33]byte) (bool, error) {
// GetNodeInfo doesn't report our private channels with the queried node
// so, we can use it to determine if the node is considered public.
nodeInfo, err := lndClient.GetNodeInfo(ctx, pubKey, true)
// so we can use it to determine if the node is considered public.
nodeInfo, err := lnd.Client.GetNodeInfo(
ctx, pubKey, true,
)
if err != nil {
return false, err
}
return nodeInfo.ChannelCount > 0, nil
return (nodeInfo.ChannelCount > 0), nil
}
// fetchChannelEdgesByID fetches the edge info for the passed channel and
// returns the channeldb structs filled with the data that is needed for
// LND's SelectHopHints implementation.
func fetchChannelEdgesByID(ctx context.Context,
lndClient lndclient.LightningClient, chanID uint64) (
*models.ChannelEdgeInfo, *models.ChannelEdgePolicy,
*models.ChannelEdgePolicy, error) {
func fetchChannelEdgesByID(ctx context.Context, lnd *lndclient.LndServices,
chanID uint64) (*channeldb.ChannelEdgeInfo, *channeldb.ChannelEdgePolicy,
*channeldb.ChannelEdgePolicy, error) {
chanInfo, err := lndClient.GetChanInfo(ctx, chanID)
chanInfo, err := lnd.Client.GetChanInfo(ctx, chanID)
if err != nil {
return nil, nil, nil, err
}
edgeInfo := &models.ChannelEdgeInfo{
edgeInfo := &channeldb.ChannelEdgeInfo{
ChannelID: chanID,
NodeKey1Bytes: chanInfo.Node1,
NodeKey2Bytes: chanInfo.Node2,
}
policy1 := &models.ChannelEdgePolicy{
policy1 := &channeldb.ChannelEdgePolicy{
FeeBaseMSat: lnwire.MilliSatoshi(
chanInfo.Node1Policy.FeeBaseMsat,
),
@ -73,7 +74,7 @@ func fetchChannelEdgesByID(ctx context.Context,
TimeLockDelta: uint16(chanInfo.Node1Policy.TimeLockDelta),
}
policy2 := &models.ChannelEdgePolicy{
policy2 := &channeldb.ChannelEdgePolicy{
FeeBaseMSat: lnwire.MilliSatoshi(
chanInfo.Node2Policy.FeeBaseMsat,
),
@ -124,14 +125,14 @@ func getAlias(aliasCache map[lnwire.ChannelID]lnwire.ShortChannelID,
// SelectHopHints calls into LND's exposed SelectHopHints prefiltered to the
// includeNodes map (unless it's empty).
func SelectHopHints(ctx context.Context, lndClient lndclient.LightningClient,
func SelectHopHints(ctx context.Context, lnd *lndclient.LndServices,
amt btcutil.Amount, numMaxHophints int,
includeNodes map[route.Vertex]struct{}) ([][]zpay32.HopHint, error) {
aliasCache := make(map[lnwire.ChannelID]lnwire.ShortChannelID)
// Fetch all active and public channels.
channels, err := lndClient.ListChannels(ctx, false, false)
channels, err := lnd.Client.ListChannels(ctx, false, false)
if err != nil {
return nil, err
}
@ -167,20 +168,20 @@ func SelectHopHints(ctx context.Context, lndClient lndclient.LightningClient,
},
)
channelID := lnwire.NewChanIDFromOutPoint(*outPoint)
channelID := lnwire.NewChanIDFromOutPoint(outPoint)
scID := lnwire.NewShortChanIDFromInt(channel.ChannelID)
aliasCache[channelID] = scID
}
cfg := &SelectHopHintsCfg{
IsPublicNode: func(pubKey [33]byte) (bool, error) {
return isPublicNode(ctx, lndClient, pubKey)
return isPublicNode(ctx, lnd, pubKey)
},
FetchChannelEdgesByID: func(chanID uint64) (
*models.ChannelEdgeInfo, *models.ChannelEdgePolicy,
*models.ChannelEdgePolicy, error) {
*channeldb.ChannelEdgeInfo, *channeldb.ChannelEdgePolicy,
*channeldb.ChannelEdgePolicy, error) {
return fetchChannelEdgesByID(ctx, lndClient, chanID)
return fetchChannelEdgesByID(ctx, lnd, chanID)
},
GetAlias: func(id lnwire.ChannelID) (
lnwire.ShortChannelID, error) {
@ -199,7 +200,7 @@ func SelectHopHints(ctx context.Context, lndClient lndclient.LightningClient,
// chanCanBeHopHint returns true if the target channel is eligible to be a hop
// hint.
func chanCanBeHopHint(channel *HopHintInfo, cfg *SelectHopHintsCfg) (
*models.ChannelEdgePolicy, bool) {
*channeldb.ChannelEdgePolicy, bool) {
// Since we're only interested in our private channels, we'll skip
// public ones.
@ -254,7 +255,7 @@ func chanCanBeHopHint(channel *HopHintInfo, cfg *SelectHopHintsCfg) (
// Now, we'll need to determine which is the correct policy for HTLCs
// being sent from the remote node.
var remotePolicy *models.ChannelEdgePolicy
var remotePolicy *channeldb.ChannelEdgePolicy
if bytes.Equal(remotePub[:], info.NodeKey1Bytes[:]) {
remotePolicy = p1
} else {
@ -267,7 +268,7 @@ func chanCanBeHopHint(channel *HopHintInfo, cfg *SelectHopHintsCfg) (
// addHopHint creates a hop hint out of the passed channel and channel policy.
// The new hop hint is appended to the passed slice.
func addHopHint(hopHints *[][]zpay32.HopHint,
channel *HopHintInfo, chanPolicy *models.ChannelEdgePolicy,
channel *HopHintInfo, chanPolicy *channeldb.ChannelEdgePolicy,
aliasScid lnwire.ShortChannelID) {
hopHint := zpay32.HopHint{
@ -330,8 +331,8 @@ type SelectHopHintsCfg struct {
// FetchChannelEdgesByID attempts to lookup the two directed edges for
// the channel identified by the channel ID.
FetchChannelEdgesByID func(chanID uint64) (*models.ChannelEdgeInfo,
*models.ChannelEdgePolicy, *models.ChannelEdgePolicy,
FetchChannelEdgesByID func(chanID uint64) (*channeldb.ChannelEdgeInfo,
*channeldb.ChannelEdgePolicy, *channeldb.ChannelEdgePolicy,
error)
// GetAlias allows the peer's alias SCID to be retrieved for private
@ -413,7 +414,7 @@ func invoicesrpcSelectHopHints(amtMSat lnwire.MilliSatoshi, cfg *SelectHopHintsC
// Lookup and see if there is an alias SCID that exists.
chanID := lnwire.NewChanIDFromOutPoint(
channel.FundingOutpoint,
&channel.FundingOutpoint,
)
alias, _ := cfg.GetAlias(chanID)
@ -470,7 +471,7 @@ func invoicesrpcSelectHopHints(amtMSat lnwire.MilliSatoshi, cfg *SelectHopHintsC
// Lookup and see if there's an alias SCID that exists.
chanID := lnwire.NewChanIDFromOutPoint(
channel.FundingOutpoint,
&channel.FundingOutpoint,
)
alias, _ := cfg.GetAlias(chanID)

@ -26,8 +26,8 @@ const semanticAlphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr
const (
// Note: please update release_notes.md when you change these values.
appMajor uint = 0
appMinor uint = 28
appPatch uint = 5
appMinor uint = 27
appPatch uint = 1
// appPreRelease MUST only contain characters from semanticAlphabet per
// the semantic versioning spec.

Loading…
Cancel
Save