mirror of
https://github.com/lightninglabs/loop.git
synced 2026-08-13 12:33:03 +02:00
Previously we may have stored negative costs for some loop out swaps which this commit attempts to correct by fetching all completed swap, calculating the correct costs and overriding them in the database.
177 lines
4.8 KiB
Go
177 lines
4.8 KiB
Go
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 = "cost_migration"
|
|
)
|
|
|
|
// 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
|
|
}
|
|
|
|
// Next we fetch all payments from LND.
|
|
payments, err := lnd.Client.ListPayments(
|
|
ctx, lndclient.ListPaymentsRequest{},
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Gather payment fees to a map for easier lookup.
|
|
paymentFees := make(map[lntypes.Hash]lnwire.MilliSatoshi)
|
|
for _, payment := range payments.Payments {
|
|
paymentFees[payment.Hash] = payment.Fee
|
|
}
|
|
|
|
// 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 {
|
|
cost, err := CalculateLoopOutCost(
|
|
lnd.ChainParams, loopOutSwap, paymentFees,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_, 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)
|
|
}
|