From c74f39f94d11e4d34e1a16a0272a74730b99ee18 Mon Sep 17 00:00:00 2001 From: kiwiidb Date: Fri, 17 Feb 2023 14:59:10 +0100 Subject: [PATCH] refactor invoice amount checks --- controllers/keysend.ctrl.go | 2 +- controllers/payinvoice.ctrl.go | 26 +++++++++----------------- controllers_v2/keysend.ctrl.go | 2 +- controllers_v2/payinvoice.ctrl.go | 2 +- db/models/invoice.go | 12 ------------ lib/service/invoices.go | 6 +++--- lib/service/invoices_test.go | 8 +++++--- lib/service/user.go | 26 ++++++++++++++++++++++++++ 8 files changed, 46 insertions(+), 38 deletions(-) diff --git a/controllers/keysend.ctrl.go b/controllers/keysend.ctrl.go index 6e12785..3e11c92 100644 --- a/controllers/keysend.ctrl.go +++ b/controllers/keysend.ctrl.go @@ -91,7 +91,7 @@ func (controller *KeySendController) KeySend(c echo.Context) error { minimumBalance := invoice.Amount if controller.svc.Config.FeeReserve { - minimumBalance += invoice.CalcFeeLimit(controller.svc.IdentityPubkey) + minimumBalance += controller.svc.CalcFeeLimit(invoice.DestinationPubkeyHex, invoice.Amount) } if currentBalance < minimumBalance { c.Logger().Errorf("User does not have enough balance invoice_id:%v user_id:%v balance:%v amount:%v", invoice.ID, userID, currentBalance, invoice.Amount) diff --git a/controllers/payinvoice.ctrl.go b/controllers/payinvoice.ctrl.go index b8242b8..8c70fe3 100644 --- a/controllers/payinvoice.ctrl.go +++ b/controllers/payinvoice.ctrl.go @@ -80,27 +80,19 @@ func (controller *PayInvoiceController) PayInvoice(c echo.Context) error { } } + ok, err := controller.svc.BalanceCheck(c.Request().Context(), lnPayReq, userID) + if err != nil { + return err + } + if !ok { + c.Logger().Errorf("User does not have enough balance user_id:%v amount:%v", userID, lnPayReq.PayReq.NumSatoshis) + return c.JSON(http.StatusBadRequest, responses.NotEnoughBalanceError) + } + invoice, err := controller.svc.AddOutgoingInvoice(c.Request().Context(), userID, paymentRequest, lnPayReq) if err != nil { return err } - - currentBalance, err := controller.svc.CurrentUserBalance(c.Request().Context(), userID) - if err != nil { - controller.svc.DB.NewDelete().Where("id = ?", invoice.ID).Exec(c.Request().Context()) - return err - } - - minimumBalance := invoice.Amount - if controller.svc.Config.FeeReserve { - minimumBalance += invoice.CalcFeeLimit(controller.svc.IdentityPubkey) - } - if currentBalance < minimumBalance { - c.Logger().Errorf("User does not have enough balance invoice_id:%v user_id:%v balance:%v amount:%v", invoice.ID, userID, currentBalance, invoice.Amount) - controller.svc.DB.NewDelete().Model(&invoice).Where("id = ?", invoice.ID).Exec(c.Request().Context()) - return c.JSON(http.StatusBadRequest, responses.NotEnoughBalanceError) - } - sendPaymentResponse, err := controller.svc.PayInvoice(c.Request().Context(), invoice) if err != nil { c.Logger().Errorf("Payment failed invoice_id:%v user_id:%v error: %v", invoice.ID, userID, err) diff --git a/controllers_v2/keysend.ctrl.go b/controllers_v2/keysend.ctrl.go index 470ef0f..9e8faf4 100644 --- a/controllers_v2/keysend.ctrl.go +++ b/controllers_v2/keysend.ctrl.go @@ -182,7 +182,7 @@ func (controller *KeySendController) SingleKeySend(c echo.Context, reqBody *KeyS minimumBalance := invoice.Amount if controller.svc.Config.FeeReserve { - minimumBalance += invoice.CalcFeeLimit(controller.svc.IdentityPubkey) + minimumBalance += controller.svc.CalcFeeLimit(invoice.DestinationPubkeyHex, invoice.Amount) } if currentBalance < minimumBalance { c.Logger().Errorf("User does not have enough balance invoice_id:%v user_id:%v balance:%v amount:%v", invoice.ID, userID, currentBalance, invoice.Amount) diff --git a/controllers_v2/payinvoice.ctrl.go b/controllers_v2/payinvoice.ctrl.go index 7dabc8f..8e18ae6 100644 --- a/controllers_v2/payinvoice.ctrl.go +++ b/controllers_v2/payinvoice.ctrl.go @@ -95,7 +95,7 @@ func (controller *PayInvoiceController) PayInvoice(c echo.Context) error { minimumBalance := invoice.Amount if controller.svc.Config.FeeReserve { - minimumBalance += invoice.CalcFeeLimit(controller.svc.IdentityPubkey) + minimumBalance += controller.svc.CalcFeeLimit(invoice.DestinationPubkeyHex, invoice.Amount) } if currentBalance < minimumBalance { c.Logger().Errorf("User does not have enough balance invoice_id:%v user_id:%v balance:%v amount:%v", invoice.ID, userID, currentBalance, invoice.Amount) diff --git a/db/models/invoice.go b/db/models/invoice.go index dfd699d..ba03e5f 100644 --- a/db/models/invoice.go +++ b/db/models/invoice.go @@ -2,7 +2,6 @@ package models import ( "context" - "math" "time" "github.com/uptrace/bun" @@ -42,15 +41,4 @@ func (i *Invoice) BeforeAppendModel(ctx context.Context, query bun.Query) error return nil } -func (i *Invoice) CalcFeeLimit(identityPubkey string) int64 { - if i.DestinationPubkeyHex == identityPubkey { - return 0 - } - limit := int64(10) - if i.Amount > 1000 { - limit = int64(math.Ceil(float64(i.Amount)*float64(0.01)) + 1) - } - return limit -} - var _ bun.BeforeAppendModelHook = (*Invoice)(nil) diff --git a/lib/service/invoices.go b/lib/service/invoices.go index a25f96f..6de503a 100644 --- a/lib/service/invoices.go +++ b/lib/service/invoices.go @@ -117,7 +117,7 @@ func (svc *LndhubService) SendInternalPayment(ctx context.Context, invoice *mode func (svc *LndhubService) SendPaymentSync(ctx context.Context, invoice *models.Invoice) (SendPaymentResponse, error) { sendPaymentResponse := SendPaymentResponse{} - sendPaymentRequest, err := createLnRpcSendRequest(invoice) + sendPaymentRequest, err := svc.createLnRpcSendRequest(invoice) if err != nil { return sendPaymentResponse, err } @@ -143,11 +143,11 @@ func (svc *LndhubService) SendPaymentSync(ctx context.Context, invoice *models.I return sendPaymentResponse, nil } -func createLnRpcSendRequest(invoice *models.Invoice) (*lnrpc.SendRequest, error) { +func (svc *LndhubService) createLnRpcSendRequest(invoice *models.Invoice) (*lnrpc.SendRequest, error) { feeLimit := lnrpc.FeeLimit{ Limit: &lnrpc.FeeLimit_Fixed{ //if we get here, the destination is never ourselves, so we can use a dummy - Fixed: invoice.CalcFeeLimit("dummy"), + Fixed: svc.CalcFeeLimit("dummy", invoice.Amount), }, } diff --git a/lib/service/invoices_test.go b/lib/service/invoices_test.go index 15ba51d..f339484 100644 --- a/lib/service/invoices_test.go +++ b/lib/service/invoices_test.go @@ -7,12 +7,14 @@ import ( "github.com/stretchr/testify/assert" ) +var svc = &LndhubService{} + func TestCalcFeeWithInvoiceLessThan1000(t *testing.T) { invoice := &models.Invoice{ Amount: 500, } - feeLimit := invoice.CalcFeeLimit("dummy") + feeLimit := svc.CalcFeeLimit("dummy", invoice.Amount) expectedFee := int64(10) assert.Equal(t, expectedFee, feeLimit) } @@ -22,7 +24,7 @@ func TestCalcFeeWithInvoiceEqualTo1000(t *testing.T) { Amount: 500, } - feeLimit := invoice.CalcFeeLimit("dummy") + feeLimit := svc.CalcFeeLimit("dummy", invoice.Amount) expectedFee := int64(10) assert.Equal(t, expectedFee, feeLimit) } @@ -32,7 +34,7 @@ func TestCalcFeeWithInvoiceMoreThan1000(t *testing.T) { Amount: 1500, } - feeLimit := invoice.CalcFeeLimit("dummy") + feeLimit := svc.CalcFeeLimit("dummy", invoice.Amount) // 1500 * 0.01 + 1 expectedFee := int64(16) assert.Equal(t, expectedFee, feeLimit) diff --git a/lib/service/user.go b/lib/service/user.go index e40995f..8022cc3 100644 --- a/lib/service/user.go +++ b/lib/service/user.go @@ -4,10 +4,12 @@ import ( "context" "database/sql" "fmt" + "math" "github.com/getAlby/lndhub.go/common" "github.com/getAlby/lndhub.go/db/models" "github.com/getAlby/lndhub.go/lib/security" + "github.com/getAlby/lndhub.go/lnd" "github.com/uptrace/bun" passwordvalidator "github.com/wagslane/go-password-validator" ) @@ -91,6 +93,30 @@ func (svc *LndhubService) FindUserByLogin(ctx context.Context, login string) (*m return &user, nil } +func (svc *LndhubService) BalanceCheck(ctx context.Context, lnpayReq *lnd.LNPayReq, userId int64) (ok bool, err error) { + currentBalance, err := svc.CurrentUserBalance(ctx, userId) + if err != nil { + return false, err + } + + minimumBalance := lnpayReq.PayReq.NumSatoshis + if svc.Config.FeeReserve { + minimumBalance += svc.CalcFeeLimit(lnpayReq.PayReq.Destination, lnpayReq.PayReq.NumSatoshis) + } + return currentBalance > minimumBalance, nil +} + +func (svc *LndhubService) CalcFeeLimit(destination string, amount int64) int64 { + if destination == svc.IdentityPubkey { + return 0 + } + limit := int64(10) + if amount > 1000 { + limit = int64(math.Ceil(float64(amount)*float64(0.01)) + 1) + } + return limit +} + func (svc *LndhubService) CurrentUserBalance(ctx context.Context, userId int64) (int64, error) { var balance int64