diff --git a/bitcoind/bitcoind.go b/bitcoind/bitcoind.go index dae3dee..bc3983d 100644 --- a/bitcoind/bitcoind.go +++ b/bitcoind/bitcoind.go @@ -5,6 +5,7 @@ import ( "log" "os" "strconv" + "time" gbitcoind "github.com/toorop/go-bitcoind" ) @@ -68,3 +69,27 @@ func GetTransaction(txid string) (*gbitcoind.RawTransaction, error) { return &rawtx, nil } + +func GetDateForHeight(height int32) (*time.Time, error) { + bitcoindPort, err := strconv.Atoi(os.Getenv("BITCOIND_PORT")) + if err != nil { + return nil, fmt.Errorf("no valid port for bitcoind: %v", os.Getenv("BITCOIND_PORT")) + } + bc, err := gbitcoind.New(os.Getenv("BITCOIND_HOST"), bitcoindPort, os.Getenv("BITCOIND_USER"), os.Getenv("BITCOIND_PASSWORD"), false) + if err != nil { + return nil, fmt.Errorf("cannot create a bitcoind client: %w", err) + } + + hash, err := bc.GetBlockHash(uint64(height)) + if err != nil { + return nil, fmt.Errorf("error getting block hash: %w", err) + } + + header, err := bc.GetBlockheader(hash) + if err != nil { + return nil, fmt.Errorf("error getting block header: %w", err) + } + + t := time.Unix(header.Time, 0) + return &t, nil +} diff --git a/db.go b/db.go index 142ad4c..e935264 100644 --- a/db.go +++ b/db.go @@ -67,6 +67,40 @@ func insertSubswapPayment(paymentHash, paymentRequest string, lockheight, confir return nil } +func getSwapsWithoutPreimage() ([]*swapper.SwapWithoutPreimage, error) { + rows, err := pgxPool.Query(context.Background(), + `SELECT payment_hash + FROM swap_payments + WHERE redeem_confirmed = false + AND payment_preimage is null + ORDER BY confirmation_height + `) + if err != nil { + return nil, fmt.Errorf("failed to query swap_payments: %w", err) + } + defer rows.Close() + + var result []*swapper.SwapWithoutPreimage + for rows.Next() { + var payment_hash string + var confirmation_height int32 + err = rows.Scan( + &payment_hash, + &confirmation_height, + ) + if err != nil { + return nil, fmt.Errorf("rows.Scan() error: %w", err) + } + + result = append(result, &swapper.SwapWithoutPreimage{ + PaymentHash: payment_hash, + ConfirmationHeight: confirmation_height, + }) + } + + return result, nil +} + func getInProgressRedeems(blockheight int32) ([]*swapper.InProgressRedeem, error) { ignoreBefore := blockheight - (288 * 14) rows, err := pgxPool.Query(context.Background(), diff --git a/server.go b/server.go index b0f7373..eee4a19 100644 --- a/server.go +++ b/server.go @@ -435,9 +435,11 @@ func main() { go registerPastBoltzReverseSwapTxNotifications() ctx, cancel := context.WithCancel(context.Background()) defer cancel() - redeemer := swapper.NewRedeemer(ssClient, ssRouterClient, subswapClient, - updateSubswapTxid, updateSubswapPreimage, getInProgressRedeems, - setSubswapConfirmed) + swapFeeService := swapper.NewFeeService() + swapFeeService.Start(ctx) + redeemer := swapper.NewRedeemer(network, ssClient, ssRouterClient, subswapClient, + swapFeeService, updateSubswapTxid, updateSubswapPreimage, + getInProgressRedeems, getSwapsWithoutPreimage, setSubswapConfirmed) redeemer.Start(ctx) lsp.InitLSP() @@ -516,8 +518,9 @@ func main() { supportServer := support.NewServer(sendPaymentFailureNotification, breezStatus, lspList) breez.RegisterSupportServer(s, supportServer) - swapperServer = swapper.NewServer(network, redisPool, client, ssClient, subswapClient, redeemer, ssWalletKitClient, ssRouterClient, - insertSubswapPayment, updateSubswapPreimage, hasFilteredAddress) + swapperServer = swapper.NewServer(network, redisPool, client, ssClient, subswapClient, redeemer, + swapFeeService, ssWalletKitClient, ssRouterClient, insertSubswapPayment, + updateSubswapPreimage, hasFilteredAddress) breez.RegisterSwapperServer(s, swapperServer) lspServer := &lsp.Server{ diff --git a/swapper/fee.go b/swapper/fee.go new file mode 100644 index 0000000..4bdefeb --- /dev/null +++ b/swapper/fee.go @@ -0,0 +1,141 @@ +package swapper + +import ( + "context" + "encoding/json" + "fmt" + "log" + "math" + "net/http" + "strconv" + "sync" + "time" +) + +type FeeService struct { + feesLastUpdated time.Time + currentFees *whatthefeeBody + mtx sync.RWMutex +} + +type whatthefeeBody struct { + Index []int32 `json:"index"` + Columns []string `json:"columns"` + Data [][]int32 `json:"data"` +} + +func NewFeeService() *FeeService { + return &FeeService{} +} + +func (f *FeeService) Start(ctx context.Context) { + go f.watchFeeRate(ctx) +} + +func (f *FeeService) GetFeeRate(blocks int32, locktime int32) (float64, error) { + f.mtx.RLock() + defer f.mtx.RUnlock() + + if f.currentFees == nil { + return 0, fmt.Errorf("still no fees") + } + + if f.feesLastUpdated.Before(time.Now().Add(-time.Hour)) { + return 0, fmt.Errorf("fees are stale") + } + + if len(f.currentFees.Index) < 1 { + return 0, fmt.Errorf("empty row index") + } + + // get the block between 0 and SwapLockTime + b := math.Min(math.Max(0, float64(blocks)), float64(locktime)) + + // certainty is linear between 0.5 and 1 based on the amount of blocks left + certainty := 0.5 + (((float64(locktime) - b) / float64(locktime)) / 2) + + // Get the row closest to the amount of blocks left + rowIndex := 0 + prevRow := f.currentFees.Index[rowIndex] + for i := 1; i < len(f.currentFees.Index); i++ { + current := f.currentFees.Index[i] + if math.Abs(float64(current)-b) < math.Abs(float64(prevRow)-b) { + rowIndex = i + prevRow = current + } + } + + if len(f.currentFees.Columns) < 1 { + return 0, fmt.Errorf("empty column index") + } + + // Get the column closest to the certainty + columnIndex := 0 + prevColumn, err := strconv.ParseFloat(f.currentFees.Columns[columnIndex], 64) + if err != nil { + return 0, fmt.Errorf("invalid column content '%s'", f.currentFees.Columns[columnIndex]) + } + for i := 1; i < len(f.currentFees.Columns); i++ { + current, err := strconv.ParseFloat(f.currentFees.Columns[i], 64) + if err != nil { + return 0, fmt.Errorf("invalid column content '%s'", f.currentFees.Columns[i]) + } + if math.Abs(current-certainty) < math.Abs(prevColumn-certainty) { + columnIndex = i + prevColumn = current + } + } + + if rowIndex >= len(f.currentFees.Data) { + return 0, fmt.Errorf("could not find fee rate column in whatthefee.io response") + } + row := f.currentFees.Data[rowIndex] + if columnIndex >= len(row) { + return 0, fmt.Errorf("could not find fee rate column in whatthefee.io response") + } + + rate := row[columnIndex] + satPerVByte := math.Exp(float64(rate) / 100) + return satPerVByte, nil +} + +func (f *FeeService) watchFeeRate(ctx context.Context) { + for { + now := time.Now() + fees, err := f.getFees() + if err != nil { + log.Printf("failed to get current chain fee rates: %v", err) + } else { + f.mtx.Lock() + f.currentFees = fees + f.feesLastUpdated = now + f.mtx.Unlock() + } + + select { + case <-time.After(time.Minute * 5): + case <-ctx.Done(): + return + } + } +} + +func (r *FeeService) getFees() (*whatthefeeBody, error) { + now := time.Now().Unix() + cacheBust := (now / 300) * 300 + resp, err := http.Get( + fmt.Sprintf("https://whatthefee.io/data.json?c=%d", cacheBust), + ) + if err != nil { + return nil, fmt.Errorf("failed to call whatthefee.io: %v", err) + } + defer resp.Body.Close() + + var body whatthefeeBody + err = json.NewDecoder(resp.Body).Decode(&body) + if err != nil { + return nil, fmt.Errorf("failed to decode whatthefee.io response: %w", err) + } + + return &body, nil +} diff --git a/swapper/redeem.go b/swapper/redeem.go index 75bfd00..121d4c2 100644 --- a/swapper/redeem.go +++ b/swapper/redeem.go @@ -5,18 +5,19 @@ import ( "context" "crypto/sha256" "encoding/hex" - "encoding/json" + "errors" "fmt" "log" - "math" - "net/http" "os" - "strconv" - "sync" "time" + "github.com/breez/server/bitcoind" "github.com/btcsuite/btcd/blockchain" + "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc/routerrpc" @@ -24,8 +25,15 @@ import ( "google.golang.org/grpc/metadata" ) -const SwapLockTime = 288 -const MinConfirmations = 6 +const ( + MinConfirmations = 6 + RedeemWitnessInputSize int32 = 1 + 1 + 73 + 1 + 32 + 1 + 100 +) + +type SwapWithoutPreimage struct { + PaymentHash string + ConfirmationHeight int32 +} type InProgressRedeem struct { PaymentHash string @@ -37,165 +45,137 @@ type InProgressRedeem struct { } type Redeemer struct { - ssClient lnrpc.LightningClient - ssRouterClient routerrpc.RouterClient - subswapClient submarineswaprpc.SubmarineSwapperClient - updateSubswapTxid func(paymentHash, txid string) error - updateSubswapPreimage func(paymentHash, paymentPreimage string) error - getInProgressRedeems func(blockheight int32) ([]*InProgressRedeem, error) - setSubswapConfirmed func(paymentHash string) error - feesLastUpdated time.Time - currentFees *whatthefeeBody - mtx sync.RWMutex + network *chaincfg.Params + ssClient lnrpc.LightningClient + ssRouterClient routerrpc.RouterClient + subswapClient submarineswaprpc.SubmarineSwapperClient + feeService *FeeService + updateSubswapTxid func(paymentHash, txid string) error + updateSubswapPreimage func(paymentHash, paymentPreimage string) error + getInProgressRedeems func(blockheight int32) ([]*InProgressRedeem, error) + getSwapsWithoutPreimage func() ([]*SwapWithoutPreimage, error) + setSubswapConfirmed func(paymentHash string) error } func NewRedeemer( + network *chaincfg.Params, ssClient lnrpc.LightningClient, ssRouterClient routerrpc.RouterClient, subswapClient submarineswaprpc.SubmarineSwapperClient, + feeService *FeeService, updateSubswapTxid func(paymentHash, txid string) error, updateSubswapPreimage func(paymentHash, paymentPreimage string) error, getInProgressRedeems func(blockheight int32) ([]*InProgressRedeem, error), + getSwapsWithoutPreimage func() ([]*SwapWithoutPreimage, error), setSubswapConfirmed func(paymentHash string) error, ) *Redeemer { return &Redeemer{ - ssClient: ssClient, - ssRouterClient: ssRouterClient, - subswapClient: subswapClient, - updateSubswapTxid: updateSubswapTxid, - updateSubswapPreimage: updateSubswapPreimage, - getInProgressRedeems: getInProgressRedeems, - setSubswapConfirmed: setSubswapConfirmed, + network: network, + ssClient: ssClient, + ssRouterClient: ssRouterClient, + subswapClient: subswapClient, + feeService: feeService, + updateSubswapTxid: updateSubswapTxid, + updateSubswapPreimage: updateSubswapPreimage, + getInProgressRedeems: getInProgressRedeems, + getSwapsWithoutPreimage: getSwapsWithoutPreimage, + setSubswapConfirmed: setSubswapConfirmed, } } func (r *Redeemer) Start(ctx context.Context) { log.Printf("REDEEM - before r.watchRedeemTxns()") go r.watchRedeemTxns(ctx) - go r.watchFeeRate(ctx) + go r.watchPreimages(ctx) } -func (r *Redeemer) watchFeeRate(ctx context.Context) { +func (r *Redeemer) watchPreimages(ctx context.Context) { for { - now := time.Now() - fees, err := r.getFees() - if err != nil { - log.Printf("failed to get current chain fee rates: %v", err) - } else { - r.mtx.Lock() - r.currentFees = fees - r.feesLastUpdated = now - r.mtx.Unlock() - } + log.Printf("REDEEM - before checkPreimages()") + r.checkPreimages() select { - case <-time.After(time.Minute * 5): + case <-time.After(time.Minute * 30): case <-ctx.Done(): return } } } -func (r *Redeemer) watchRedeemTxns(ctx context.Context) { - for { - log.Printf("REDEEM - before checkRedeems()") - r.checkRedeems() - - select { - case <-time.After(time.Minute * 5): - case <-ctx.Done(): - return - } +func (r *Redeemer) checkPreimages() { + swaps, err := r.getSwapsWithoutPreimage() + if err != nil { + log.Printf("checkPreimages - Failed to getSwapsWithoutPreimage: %v", err) + return } -} -type whatthefeeBody struct { - Index []int32 `json:"index"` - Columns []string `json:"columns"` - Data [][]int32 `json:"data"` -} - -func (r *Redeemer) getFees() (*whatthefeeBody, error) { - now := time.Now().Unix() - cacheBust := (now / 300) * 300 - resp, err := http.Get( - fmt.Sprintf("https://whatthefee.io/data.json?c=%d", cacheBust), - ) - if err != nil { - return nil, fmt.Errorf("failed to call whatthefee.io: %v", err) + if len(swaps) == 0 { + return } - defer resp.Body.Close() - var body whatthefeeBody - err = json.NewDecoder(resp.Body).Decode(&body) + earliestHeight := swaps[0].ConfirmationHeight + t, err := bitcoind.GetDateForHeight(earliestHeight) if err != nil { - return nil, fmt.Errorf("failed to decode whatthefee.io response: %w", err) + log.Printf("checkPreimages - Failed to date for height %d: %v", earliestHeight, err) + return } - return &body, nil -} - -func (r *Redeemer) getFeeRate(blocks int32) (float64, error) { - r.mtx.RLock() - defer r.mtx.RUnlock() - - if r.currentFees == nil { - return 0, fmt.Errorf("still no fees") + // substract a day for leeway + time := t.Add(-time.Hour * 24) + log.Printf("checkPreimages - getting payments after %s", time.String()) + subswapClientCtx := metadata.AppendToOutgoingContext(context.Background(), "macaroon", os.Getenv("SUBSWAPPER_LND_MACAROON_HEX")) + payments, err := r.ssClient.ListPayments(subswapClientCtx, &lnrpc.ListPaymentsRequest{ + CreationDateStart: uint64(time.Unix()), + }) + if err != nil { + log.Printf("checkPreimages - Failed to ListPayments: %v", err) + return } + log.Printf("checkPreimages - getting payments after %s yielded %d payments", time.String(), len(payments.Payments)) - if len(r.currentFees.Index) < 1 { - return 0, fmt.Errorf("empty row index") + swapLookup := make(map[string]bool, 0) + for _, swap := range swaps { + swapLookup[swap.PaymentHash] = true } - // get the block between 0 and SwapLockTime - b := math.Min(math.Max(0, float64(blocks)), SwapLockTime) - - // certainty is linear between 0.5 and 1 based on the amount of blocks left - certainty := 0.5 + (((SwapLockTime - b) / SwapLockTime) / 2) + newPreimages := make(map[string]string, 0) + for _, payment := range payments.Payments { + if payment.PaymentPreimage == "" { + continue + } - // Get the row closest to the amount of blocks left - rowIndex := 0 - prevRow := r.currentFees.Index[rowIndex] - for i := 1; i < len(r.currentFees.Index); i++ { - current := r.currentFees.Index[i] - if math.Abs(float64(current)-b) < math.Abs(float64(prevRow)-b) { - rowIndex = i - prevRow = current + _, ok := swapLookup[payment.PaymentHash] + if !ok { + continue } - } - if len(r.currentFees.Columns) < 1 { - return 0, fmt.Errorf("empty column index") + newPreimages[payment.PaymentHash] = payment.PaymentPreimage } - // Get the column closest to the certainty - columnIndex := 0 - prevColumn, err := strconv.ParseFloat(r.currentFees.Columns[columnIndex], 64) - if err != nil { - return 0, fmt.Errorf("invalid column content '%s'", r.currentFees.Columns[columnIndex]) + if len(newPreimages) == 0 { + log.Printf("checkPreimages - no new preimages") + return } - for i := 1; i < len(r.currentFees.Columns); i++ { - current, err := strconv.ParseFloat(r.currentFees.Columns[i], 64) + + for paymentHash, preimage := range newPreimages { + err = r.updateSubswapPreimage(paymentHash, preimage) if err != nil { - return 0, fmt.Errorf("invalid column content '%s'", r.currentFees.Columns[i]) - } - if math.Abs(current-certainty) < math.Abs(prevColumn-certainty) { - columnIndex = i - prevColumn = current + log.Printf("checkPreimages - failed to update preimage %s for payment hash %s", preimage, paymentHash) } } +} - if rowIndex >= len(r.currentFees.Data) { - return 0, fmt.Errorf("could not find fee rate column in whatthefee.io response") - } - row := r.currentFees.Data[rowIndex] - if columnIndex >= len(row) { - return 0, fmt.Errorf("could not find fee rate column in whatthefee.io response") - } +func (r *Redeemer) watchRedeemTxns(ctx context.Context) { + for { + log.Printf("REDEEM - before checkRedeems()") + r.checkRedeems() - rate := row[columnIndex] - satPerVByte := math.Exp(float64(rate) / 100) - return satPerVByte, nil + select { + case <-time.After(time.Minute * 5): + case <-ctx.Done(): + return + } + } } func (r *Redeemer) checkRedeems() { @@ -274,7 +254,7 @@ func (r *Redeemer) checkRedeem(blockHeight int32, inProgressRedeem *InProgressRe if len(txns) == 0 { log.Printf("RedeemWithinBlocks - preimage: %x, blocksLeft: %v", preimage, blocksLeft) return nil - // _, err := r.RedeemWithinBlocks(preimage, blocksLeft) + // _, err := r.RedeemWithinBlocks(preimage, blocksLeft, inProgressRedeem.LockHeight) // return err } @@ -308,13 +288,13 @@ func (r *Redeemer) checkRedeem(blockHeight int32, inProgressRedeem *InProgressRe } } - satPerVbyte, err := r.getFeeRate(blocksLeft) + satPerVbyte, err := r.feeService.GetFeeRate(blocksLeft, inProgressRedeem.LockHeight) if err != nil { log.Printf("failed to get redeem fee rate: %v", err) // If there is a problem getting the fees, try to bump the tx on best effort. log.Printf("RedeemWithinBlocks - preimage: %x, blocksLeft: %v", preimage, blocksLeft) return nil - // _, err = r.RedeemWithinBlocks(preimage, blocksLeft) + // _, err = r.RedeemWithinBlocks(preimage, blocksLeft, inProgressRedeem.LockHeight) // return err } @@ -347,8 +327,8 @@ func getWeight(tx *lnrpc.Transaction) (int64, error) { return weight, nil } -func (r *Redeemer) RedeemWithinBlocks(preimage []byte, blocks int32) (string, error) { - rate, err := r.getFeeRate(blocks) +func (r *Redeemer) RedeemWithinBlocks(preimage []byte, blocks int32, locktime int32) (string, error) { + rate, err := r.feeService.GetFeeRate(blocks, locktime) if err != nil { log.Printf("RedeemWithinBlocks(%x, %d) - getFeeRate error: %v", preimage, blocks, err) } @@ -386,3 +366,50 @@ func (r *Redeemer) doRedeem(preimage []byte, targetConf int32, satPerByte int64) return redeem.Txid, err } + +func (r *Redeemer) RedeemWeight(utxos []*submarineswaprpc.UnspentAmountResponse_Utxo) (int32, error) { + if len(utxos) == 0 { + return 0, errors.New("no utxo") + } + + redeemTx := wire.NewMsgTx(1) + + // Add the inputs without the witness and calculate the amount to redeem + var amount btcutil.Amount + for _, utxo := range utxos { + txid, err := chainhash.NewHashFromStr(utxo.Txid) + if err != nil { + return 0, fmt.Errorf("failed to parse txid: %w", err) + } + outpoint := &wire.OutPoint{ + Hash: *txid, + Index: utxo.Index, + } + amount += btcutil.Amount(utxo.Amount) + txIn := wire.NewTxIn(outpoint, nil, nil) + txIn.Sequence = 0 + redeemTx.AddTxIn(txIn) + } + + //Generate a random address + privateKey, err := btcec.NewPrivateKey() + if err != nil { + return 0, err + } + redeemAddress, err := btcutil.NewAddressPubKey(privateKey.PubKey().SerializeCompressed(), r.network) + if err != nil { + return 0, err + } + // Add the single output + redeemScript, err := txscript.PayToAddrScript(redeemAddress) + if err != nil { + return 0, err + } + txOut := wire.TxOut{PkScript: redeemScript} + redeemTx.AddTxOut(&txOut) + redeemTx.LockTime = uint32(833000) // fake block height + + // Calcluate the weight and the fee + weight := 4*int32(redeemTx.SerializeSizeStripped()) + RedeemWitnessInputSize*int32(len(redeemTx.TxIn)) + return weight, nil +} diff --git a/swapper/swapper.go b/swapper/swapper.go index c403511..5dc288c 100644 --- a/swapper/swapper.go +++ b/swapper/swapper.go @@ -5,6 +5,7 @@ import ( "encoding/hex" "fmt" "log" + "math" "os" "strconv" "strings" @@ -40,6 +41,7 @@ type Server struct { ssClient lnrpc.LightningClient subswapClient submarineswaprpc.SubmarineSwapperClient redeemer *Redeemer + feeService *FeeService walletKitClient walletrpc.WalletKitClient ssRouterClient routerrpc.RouterClient insertSubswapPayment func(paymentHash, paymentRequest string, lockheight, confirmationheight int32, utxos []string) error @@ -53,6 +55,7 @@ func NewServer( client, ssClient lnrpc.LightningClient, subswapClient submarineswaprpc.SubmarineSwapperClient, redeemer *Redeemer, + feeService *FeeService, walletKitClient walletrpc.WalletKitClient, ssRouterClient routerrpc.RouterClient, insertSubswapPayment func(paymentHash, paymentRequest string, lockheight, confirmationheight int32, utxos []string) error, @@ -66,6 +69,7 @@ func NewServer( ssClient: ssClient, subswapClient: subswapClient, redeemer: redeemer, + feeService: feeService, walletKitClient: walletKitClient, ssRouterClient: ssRouterClient, insertSubswapPayment: insertSubswapPayment, @@ -111,14 +115,13 @@ func (s *Server) addFundInit(ctx context.Context, in *breez.AddFundInitRequest, } var minAllowedDeposit int64 - ct := 12 - fees, err := s.walletKitClient.EstimateFee(clientCtx, &walletrpc.EstimateFeeRequest{ConfTarget: int32(ct)}) + fees, err := s.feeService.GetFeeRate(3, 288) if err != nil { - log.Printf("walletKitClient.EstimateFee(%v) error: %v", ct, err) + log.Printf("feeService.GetFeeRate(3, 288) error: %v", err) } else { - log.Printf("walletKitClient.EstimateFee(%v): %v", ct, fees.SatPerKw) + log.Printf("feeService.GetFeeRate(3, 288): %v sat/vbyte", fees) // Assume a weight of 1K for the transaction. - minAllowedDeposit = fees.SatPerKw * 3 / 2 + minAllowedDeposit = int64(fees * 250 * 3 / 2) } address := subSwapServiceInitResponse.Address @@ -230,16 +233,19 @@ func (s *Server) getSwapPayment(ctx context.Context, in *breez.GetSwapPaymentReq return nil, status.Errorf(codes.Internal, "there are no UTXOs related to payment request") } - fees, err := s.subswapClient.SubSwapServiceRedeemFees(subswapClientCtx, &submarineswaprpc.SubSwapServiceRedeemFeesRequest{ - Hash: decodedPayReq.PaymentHash[:], - TargetConf: 30, - }) + satPerVbyte, err := s.feeService.GetFeeRate(3, utxos.LockHeight) + if err != nil { + log.Printf("GetSwapPayment - GetFeeRate error: %v", err) + return nil, status.Errorf(codes.Internal, "couldn't determine the redeem transaction fees") + } + weight, err := s.redeemer.RedeemWeight(utxos.Utxos) if err != nil { - log.Printf("GetSwapPayment - SubSwapServiceRedeemFees error: %v", err) + log.Printf("GetSwapPayment - RedeemWeight error: %v", err) return nil, status.Errorf(codes.Internal, "couldn't determine the redeem transaction fees") } - log.Printf("GetSwapPayment - SubSwapServiceRedeemFees: %v for amount in utxos: %v amount in payment request: %v", fees.Amount, utxos.Amount, decodedAmt) - if 2*utxos.Amount < 3*fees.Amount { + fees := int64(math.Ceil((satPerVbyte * float64(weight)) / 4)) + log.Printf("GetSwapPayment - SubSwapServiceRedeemFees: %v for amount in utxos: %v amount in payment request: %v", fees, utxos.Amount, decodedAmt) + if 2*utxos.Amount < 3*fees { log.Println("GetSwapPayment - utxo amount less than 1.5 fees. Cannot proceed") return &breez.GetSwapPaymentReply{ FundsExceededLimit: true, @@ -325,7 +331,7 @@ func (s *Server) getSwapPayment(ctx context.Context, in *breez.GetSwapPaymentReq log.Printf("Failed to update subswap preimage '%x' for payment hash '%x', error: %v", sendResponse.PaymentPreimage, sendResponse.PaymentHash, err) } - _, err = s.redeemer.RedeemWithinBlocks(sendResponse.PaymentPreimage, int32(chainInfo.BlockHeight)-minHeight) + _, err = s.redeemer.RedeemWithinBlocks(sendResponse.PaymentPreimage, int32(chainInfo.BlockHeight)-minHeight, utxos.LockHeight) if err != nil { log.Printf("RedeemWithinBlocks - couldn't redeem transaction for preimage: %x, error: %v", sendResponse.PaymentPreimage, err) }