fix merge conflict

This commit is contained in:
kiwiidb
2022-03-08 14:57:46 +01:00
13 changed files with 348 additions and 170 deletions

View File

@@ -35,7 +35,8 @@ func (controller *AuthController) Auth(c echo.Context) error {
var body AuthRequestBody
if err := c.Bind(&body); err != nil {
return err
c.Logger().Errorf("Failed to load auth user request body: %v", err)
return c.JSON(http.StatusBadRequest, responses.BadArgumentsError)
}
if err := c.Validate(&body); err != nil {
return c.JSON(http.StatusBadRequest, responses.BadArgumentsError)

View File

@@ -3,6 +3,7 @@ package controllers
import (
"net/http"
"github.com/getAlby/lndhub.go/lib/responses"
"github.com/getAlby/lndhub.go/lib/service"
"github.com/labstack/echo/v4"
)
@@ -33,12 +34,13 @@ func (controller *CreateUserController) CreateUser(c echo.Context) error {
var body CreateUserRequestBody
if err := c.Bind(&body); err != nil {
return err
c.Logger().Errorf("Failed to load create user request body: %v", err)
return c.JSON(http.StatusBadRequest, responses.BadArgumentsError)
}
user, err := controller.svc.CreateUser(c.Request().Context(), body.Login, body.Password)
//todo json response
if err != nil {
return err
c.Logger().Errorf("Failed to create user: %v", err)
return c.JSON(http.StatusBadRequest, responses.BadArgumentsError)
}
var ResponseBody CreateUserResponseBody

102
controllers/keysend.ctrl.go Normal file
View File

@@ -0,0 +1,102 @@
package controllers
import (
"fmt"
"net/http"
"github.com/getAlby/lndhub.go/lib"
"github.com/getAlby/lndhub.go/lib/responses"
"github.com/getAlby/lndhub.go/lib/service"
"github.com/getAlby/lndhub.go/lnd"
"github.com/getsentry/sentry-go"
"github.com/labstack/echo/v4"
"github.com/lightningnetwork/lnd/lnrpc"
)
// KeySendController : Key send controller struct
type KeySendController struct {
svc *service.LndhubService
}
func NewKeySendController(svc *service.LndhubService) *KeySendController {
return &KeySendController{svc: svc}
}
type KeySendRequestBody struct {
Amount int64 `json:"amount" validate:"required"`
Destination string `json:"destination" validate:"required"`
Memo string `json:"memo" validate:"omitempty"`
}
type KeySendResponseBody struct {
RHash *lib.JavaScriptBuffer `json:"payment_hash,omitempty"`
Amount int64 `json:"num_satoshis,omitempty"`
Description string `json:"description,omitempty"`
Destination string `json:"destination,omitempty"`
DescriptionHashStr string `json:"description_hash,omitempty"`
PaymentError string `json:"payment_error,omitempty"`
PaymentPreimage *lib.JavaScriptBuffer `json:"payment_preimage,omitempty"`
PaymentRoute *service.Route `json:"route,omitempty"`
}
// KeySend : Key send Controller
func (controller *KeySendController) KeySend(c echo.Context) error {
userID := c.Get("UserID").(int64)
reqBody := KeySendRequestBody{}
if err := c.Bind(&reqBody); err != nil {
c.Logger().Errorf("Failed to load keysend request body: %v", err)
return c.JSON(http.StatusBadRequest, responses.BadArgumentsError)
}
if err := c.Validate(&reqBody); err != nil {
c.Logger().Errorf("Invalid keysend request body: %v", err)
return c.JSON(http.StatusBadRequest, responses.BadArgumentsError)
}
lnPayReq := &lnd.LNPayReq{
PayReq: &lnrpc.PayReq{
Destination: reqBody.Destination,
NumSatoshis: reqBody.Amount,
Description: reqBody.Memo,
},
Keysend: true,
}
invoice, err := controller.svc.AddOutgoingInvoice(c.Request().Context(), userID, "", lnPayReq)
if err != nil {
return err
}
currentBalance, err := controller.svc.CurrentUserBalance(c.Request().Context(), userID)
if err != nil {
return err
}
if currentBalance < invoice.Amount {
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)
return c.JSON(http.StatusBadRequest, responses.NotEnoughBalanceError)
}
sendPaymentResponse, err := controller.svc.PayInvoice(c.Request().Context(), invoice)
if err != nil {
c.Logger().Errorf("Payment failed: %v", err)
sentry.CaptureException(err)
return c.JSON(http.StatusBadRequest, echo.Map{
"error": true,
"code": 10,
"message": fmt.Sprintf("Payment failed. Does the receiver have enough inbound capacity? (%v)", err),
})
}
responseBody := &KeySendResponseBody{}
responseBody.RHash = &lib.JavaScriptBuffer{Data: sendPaymentResponse.PaymentHash}
responseBody.Amount = invoice.Amount
responseBody.Destination = invoice.DestinationPubkeyHex
responseBody.Description = invoice.Memo
responseBody.DescriptionHashStr = invoice.DescriptionHash
responseBody.PaymentError = sendPaymentResponse.PaymentError
responseBody.PaymentPreimage = &lib.JavaScriptBuffer{Data: sendPaymentResponse.PaymentPreimage}
responseBody.PaymentRoute = sendPaymentResponse.PaymentRoute
return c.JSON(http.StatusOK, responseBody)
}

View File

@@ -7,6 +7,7 @@ import (
"github.com/getAlby/lndhub.go/lib"
"github.com/getAlby/lndhub.go/lib/responses"
"github.com/getAlby/lndhub.go/lib/service"
"github.com/getAlby/lndhub.go/lnd"
"github.com/getsentry/sentry-go"
"github.com/labstack/echo/v4"
)
@@ -69,7 +70,12 @@ func (controller *PayInvoiceController) PayInvoice(c echo.Context) error {
}
*/
invoice, err := controller.svc.AddOutgoingInvoice(c.Request().Context(), userID, paymentRequest, decodedPaymentRequest)
lnPayReq := &lnd.LNPayReq{
PayReq: decodedPaymentRequest,
Keysend: false,
}
invoice, err := controller.svc.AddOutgoingInvoice(c.Request().Context(), userID, paymentRequest, lnPayReq)
if err != nil {
return err
}

View File

@@ -0,0 +1 @@
alter table invoices add column keysend boolean;

View File

@@ -22,6 +22,7 @@ type Invoice struct {
RHash string `json:"r_hash"`
Preimage string `json:"preimage" bun:",nullzero"`
Internal bool `json:"internal" bun:",nullzero"`
Keysend bool `json:"keysend" bun:",nullzero"`
State string `json:"state" bun:",default:'initialized'"`
ErrorMessage string `json:"error_message" bun:",nullzero"`
AddIndex uint64 `json:"add_index" bun:",nullzero"`

145
go.mod
View File

@@ -1,10 +1,10 @@
module github.com/getAlby/lndhub.go
go 1.17
// +heroku goVersion go1.17
require (
github.com/btcsuite/btcd v0.22.0-beta.0.20211005184431-e3449998be39
github.com/getsentry/sentry-go v0.12.0
github.com/go-playground/validator/v10 v10.10.0
github.com/golang-jwt/jwt v3.2.2+incompatible
@@ -27,149 +27,8 @@ require (
)
require (
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect
github.com/aead/siphash v1.0.1 // indirect
github.com/andybalholm/brotli v1.0.3 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f // indirect
github.com/btcsuite/btcutil v1.0.3-0.20210527170813-e2ba6805a890 // indirect
github.com/btcsuite/btcutil/psbt v1.0.3-0.20210527170813-e2ba6805a890 // indirect
github.com/btcsuite/btcwallet v0.13.0 // indirect
github.com/btcsuite/btcwallet/wallet/txauthor v1.1.0 // indirect
github.com/btcsuite/btcwallet/wallet/txrules v1.1.0 // indirect
github.com/btcsuite/btcwallet/wallet/txsizes v1.1.0 // indirect
github.com/btcsuite/btcwallet/walletdb v1.3.6-0.20210803004036-eebed51155ec // indirect
github.com/btcsuite/btcwallet/wtxmgr v1.3.1-0.20210822222949-9b5a201c344c // indirect
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd // indirect
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 // indirect
github.com/cespare/xxhash/v2 v2.1.1 // indirect
github.com/coreos/go-semver v0.3.0 // indirect
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/decred/dcrd/lru v1.0.0 // indirect
github.com/dsnet/compress v0.0.1 // indirect
github.com/dustin/go-humanize v1.0.0 // indirect
github.com/fatih/color v1.13.0 // indirect
github.com/fergusstrange/embedded-postgres v1.10.0 // indirect
github.com/form3tech-oss/jwt-go v3.2.3+incompatible // indirect
github.com/go-errors/errors v1.0.1 // indirect
github.com/go-playground/locales v0.14.0 // indirect
github.com/go-playground/universal-translator v0.18.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/btree v1.0.1 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/gorilla/websocket v1.4.2 // 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/grpc-ecosystem/grpc-gateway/v2 v2.5.0 // indirect
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
github.com/jackc/pgconn v1.10.0 // indirect
github.com/jackc/pgio v1.0.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgproto3/v2 v2.1.1 // indirect
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
github.com/jackc/pgtype v1.8.1 // indirect
github.com/jackc/pgx/v4 v4.13.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jonboulle/clockwork v0.2.2 // indirect
github.com/jrick/logrotate v1.0.0 // indirect
github.com/json-iterator/go v1.1.11 // indirect
github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8 // 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.13.6 // indirect
github.com/klauspost/pgzip v1.2.5 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/lib/pq v1.10.3 // indirect
github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf // indirect
github.com/lightninglabs/neutrino v0.13.0 // indirect
github.com/lightningnetwork/lightning-onion v1.0.2-0.20210520211913-522b799e65b1 // indirect
github.com/lightningnetwork/lnd/clock v1.1.0 // indirect
github.com/lightningnetwork/lnd/healthcheck v1.2.0 // indirect
github.com/lightningnetwork/lnd/kvdb v1.2.1 // indirect
github.com/lightningnetwork/lnd/queue v1.1.0 // indirect
github.com/lightningnetwork/lnd/ticker v1.1.0 // indirect
github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mattn/go-sqlite3 v1.14.10 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/mholt/archiver/v3 v3.5.0 // indirect
github.com/miekg/dns v1.1.43 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.1 // indirect
github.com/nwaples/rardecode v1.1.2 // indirect
github.com/pierrec/lz4/v4 v4.1.8 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.11.0 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.26.0 // indirect
github.com/prometheus/procfs v0.6.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
github.com/rogpeppe/fastuuid v1.2.0 // indirect
github.com/rs/zerolog v1.26.0 // indirect
github.com/sirupsen/logrus v1.7.0 // indirect
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect
github.com/soheilhy/cmux v0.1.5 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 // indirect
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect
github.com/ulikunitz/xz v0.5.10 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.1 // indirect
github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect
go.etcd.io/bbolt v1.3.6 // indirect
go.etcd.io/etcd/api/v3 v3.5.0 // indirect
go.etcd.io/etcd/client/pkg/v3 v3.5.0 // indirect
go.etcd.io/etcd/client/v2 v2.305.0 // indirect
go.etcd.io/etcd/client/v3 v3.5.0 // indirect
go.etcd.io/etcd/pkg/v3 v3.5.0 // indirect
go.etcd.io/etcd/raft/v3 v3.5.0 // indirect
go.etcd.io/etcd/server/v3 v3.5.0 // indirect
go.opentelemetry.io/contrib v0.20.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0 // indirect
go.opentelemetry.io/otel v0.20.0 // indirect
go.opentelemetry.io/otel/exporters/otlp v0.20.0 // indirect
go.opentelemetry.io/otel/metric v0.20.0 // indirect
go.opentelemetry.io/otel/sdk v0.20.0 // indirect
go.opentelemetry.io/otel/sdk/export/metric v0.20.0 // indirect
go.opentelemetry.io/otel/sdk/metric v0.20.0 // indirect
go.opentelemetry.io/otel/trace v0.20.0 // indirect
go.opentelemetry.io/proto/otlp v0.7.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/mod v0.5.1 // indirect
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
golang.org/x/net v0.0.0-20220114011407-0dd24b26b47d // indirect
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 // indirect
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect
golang.org/x/tools v0.1.8 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
google.golang.org/genproto v0.0.0-20220114231437-d2e6a121cae0 // indirect
google.golang.org/protobuf v1.27.1 // indirect
gopkg.in/errgo.v1 v1.0.1 // indirect
gopkg.in/macaroon-bakery.v2 v2.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.0-20210107192922-496545a6307b // indirect
lukechampine.com/uint128 v1.1.1 // indirect
mellium.im/sasl v0.2.1 // indirect
modernc.org/cc/v3 v3.35.22 // indirect
modernc.org/ccgo/v3 v3.14.0 // indirect
modernc.org/libc v1.13.2 // indirect
modernc.org/mathutil v1.4.1 // indirect
modernc.org/memory v1.0.5 // indirect
modernc.org/opt v0.1.1 // indirect
modernc.org/sqlite v1.14.3 // indirect
modernc.org/strutil v1.1.1 // indirect
modernc.org/token v1.0.0 // indirect
sigs.k8s.io/yaml v1.2.0 // indirect
)

View File

@@ -0,0 +1,122 @@
package integration_tests
import (
"context"
"fmt"
"log"
"testing"
"time"
"github.com/getAlby/lndhub.go/controllers"
"github.com/getAlby/lndhub.go/lib"
"github.com/getAlby/lndhub.go/lib/responses"
"github.com/getAlby/lndhub.go/lib/service"
"github.com/getAlby/lndhub.go/lib/tokens"
"github.com/getAlby/lndhub.go/lnd"
"github.com/go-playground/validator/v10"
"github.com/labstack/echo/v4"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
)
type KeySendTestSuite struct {
TestSuite
fundingClient *lnd.LNDWrapper
service *service.LndhubService
aliceLogin controllers.CreateUserResponseBody
aliceToken string
invoiceUpdateSubCancelFn context.CancelFunc
}
func (suite *KeySendTestSuite) SetupSuite() {
lndClient, err := lnd.NewLNDclient(lnd.LNDoptions{
Address: lnd2RegtestAddress,
MacaroonHex: lnd2RegtestMacaroonHex,
})
if err != nil {
log.Fatalf("Error setting up funding client: %v", err)
}
suite.fundingClient = lndClient
svc, err := LndHubTestServiceInit(nil)
if err != nil {
log.Fatalf("Error initializing test service: %v", err)
}
users, userTokens, err := createUsers(svc, 1)
if err != nil {
log.Fatalf("Error creating test users: %v", err)
}
// Subscribe to LND invoice updates in the background
// store cancel func to be called in tear down suite
ctx, cancel := context.WithCancel(context.Background())
suite.invoiceUpdateSubCancelFn = cancel
go svc.InvoiceUpdateSubscription(ctx)
suite.service = svc
e := echo.New()
e.HTTPErrorHandler = responses.HTTPErrorHandler
e.Validator = &lib.CustomValidator{Validator: validator.New()}
suite.echo = e
assert.Equal(suite.T(), 1, len(users))
assert.Equal(suite.T(), 1, len(userTokens))
suite.aliceLogin = users[0]
suite.aliceToken = userTokens[0]
suite.echo.Use(tokens.Middleware([]byte(suite.service.Config.JWTSecret)))
suite.echo.GET("/balance", controllers.NewBalanceController(suite.service).Balance)
suite.echo.POST("/addinvoice", controllers.NewAddInvoiceController(suite.service).AddInvoice)
suite.echo.POST("/payinvoice", controllers.NewPayInvoiceController(suite.service).PayInvoice)
suite.echo.POST("/keysend", controllers.NewKeySendController(suite.service).KeySend)
}
func (suite *KeySendTestSuite) TearDownSuite() {
suite.invoiceUpdateSubCancelFn()
}
func (suite *KeySendTestSuite) TestKeysendPayment() {
aliceFundingSats := 1000
externalSatRequested := 500
//fund alice account
invoiceResponse := suite.createAddInvoiceReq(aliceFundingSats, "integration test external payment alice", suite.aliceToken)
sendPaymentRequest := lnrpc.SendRequest{
PaymentRequest: invoiceResponse.PayReq,
FeeLimit: nil,
}
_, err := suite.fundingClient.SendPaymentSync(context.Background(), &sendPaymentRequest)
assert.NoError(suite.T(), err)
//wait a bit for the callback event to hit
time.Sleep(100 * time.Millisecond)
suite.createKeySendReq(int64(externalSatRequested), "key send test", simnetLnd3PubKey, suite.aliceToken)
// check that balance was reduced
userId := getUserIdFromToken(suite.aliceToken)
aliceBalance, err := suite.service.CurrentUserBalance(context.Background(), userId)
if err != nil {
fmt.Printf("Error when getting balance %v\n", err.Error())
}
assert.Equal(suite.T(), int64(aliceFundingSats)-int64(externalSatRequested), aliceBalance)
}
func (suite *KeySendTestSuite) TestKeysendPaymentNonExistentDestination() {
aliceFundingSats := 1000
externalSatRequested := 500
//fund alice account
invoiceResponse := suite.createAddInvoiceReq(aliceFundingSats, "integration test external payment alice", suite.aliceToken)
sendPaymentRequest := lnrpc.SendRequest{
PaymentRequest: invoiceResponse.PayReq,
FeeLimit: nil,
}
_, err := suite.fundingClient.SendPaymentSync(context.Background(), &sendPaymentRequest)
assert.NoError(suite.T(), err)
//wait a bit for the callback event to hit
time.Sleep(100 * time.Millisecond)
suite.createKeySendReqError(int64(externalSatRequested), "key send test", "12345", suite.aliceToken)
}
func TestKeySendTestSuite(t *testing.T) {
suite.Run(t, new(KeySendTestSuite))
}

View File

@@ -33,6 +33,9 @@ const (
// This will cause payment to be routed through lnd2, which will charge a fee (lnd default fee 1 sat base + 1 ppm).
lnd3RegtestAddress = "rpc.lnd3.regtest.getalby.com:443"
lnd3RegtestMacaroonHex = "0201036c6e6402f801030a102a5aa69a5efdf4b4a55a5304b164641f1201301a160a0761646472657373120472656164120577726974651a130a04696e666f120472656164120577726974651a170a08696e766f69636573120472656164120577726974651a210a086d616361726f6f6e120867656e6572617465120472656164120577726974651a160a076d657373616765120472656164120577726974651a170a086f6666636861696e120472656164120577726974651a160a076f6e636861696e120472656164120577726974651a140a057065657273120472656164120577726974651a180a067369676e6572120867656e657261746512047265616400000620defbb5a809262297fd661a9ab6d3deb4b7acca4f1309c79addb952f0dc2d8c82"
simnetLnd1PubKey = "0242898f86064c2fd72de22059c947a83ba23e9d97aedeae7b6dba647123f1d71b"
simnetLnd2PubKey = "025c1d5d1b4c983cc6350fc2d756fbb59b4dc365e45e87f8e3afe07e24013e8220"
simnetLnd3PubKey = "03c7092d076f799ab18806743634b4c9bb34e351bdebc91d5b35963f3dc63ec5aa"
)
func LndHubTestServiceInit(lndClientMock lnd.LightningClientWrapper) (svc *service.LndhubService, err error) {
@@ -154,6 +157,44 @@ func (suite *TestSuite) createAddInvoiceReq(amt int, memo, token string) *contro
return invoiceResponse
}
func (suite *TestSuite) createKeySendReq(amount int64, memo, destination, token string) *controllers.KeySendResponseBody {
rec := httptest.NewRecorder()
var buf bytes.Buffer
assert.NoError(suite.T(), json.NewEncoder(&buf).Encode(&controllers.KeySendRequestBody{
Amount: amount,
Destination: destination,
Memo: memo,
}))
req := httptest.NewRequest(http.MethodPost, "/keysend", &buf)
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token))
suite.echo.ServeHTTP(rec, req)
keySendResponse := &controllers.KeySendResponseBody{}
assert.Equal(suite.T(), http.StatusOK, rec.Code)
assert.NoError(suite.T(), json.NewDecoder(rec.Body).Decode(keySendResponse))
return keySendResponse
}
func (suite *TestSuite) createKeySendReqError(amount int64, memo, destination, token string) *responses.ErrorResponse {
rec := httptest.NewRecorder()
var buf bytes.Buffer
assert.NoError(suite.T(), json.NewEncoder(&buf).Encode(&controllers.KeySendRequestBody{
Amount: amount,
Destination: destination,
Memo: memo,
}))
req := httptest.NewRequest(http.MethodPost, "/keysend", &buf)
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token))
suite.echo.ServeHTTP(rec, req)
errorResponse := &responses.ErrorResponse{}
assert.Equal(suite.T(), http.StatusBadRequest, rec.Code)
assert.NoError(suite.T(), json.NewDecoder(rec.Body).Decode(errorResponse))
return errorResponse
}
func (suite *TestSuite) createPayInvoiceReq(payReq string, token string) *controllers.PayInvoiceResponseBody {
rec := httptest.NewRecorder()
var buf bytes.Buffer

View File

@@ -2,6 +2,7 @@ package service
import (
"context"
"crypto/sha256"
"encoding/hex"
"errors"
"fmt"
@@ -10,6 +11,7 @@ import (
"github.com/getAlby/lndhub.go/common"
"github.com/getAlby/lndhub.go/db/models"
"github.com/getAlby/lndhub.go/lnd"
"github.com/getsentry/sentry-go"
"github.com/labstack/gommon/random"
"github.com/lightningnetwork/lnd/lnrpc"
@@ -100,25 +102,14 @@ func (svc *LndhubService) SendInternalPayment(ctx context.Context, invoice *mode
func (svc *LndhubService) SendPaymentSync(ctx context.Context, invoice *models.Invoice) (SendPaymentResponse, error) {
sendPaymentResponse := SendPaymentResponse{}
// TODO: set dynamic fee limit
feeLimit := lnrpc.FeeLimit{
//Limit: &lnrpc.FeeLimit_Percent{
// Percent: 2,
//},
Limit: &lnrpc.FeeLimit_Fixed{
Fixed: 300,
},
}
// Prepare the LNRPC call
sendPaymentRequest := lnrpc.SendRequest{
PaymentRequest: invoice.PaymentRequest,
Amt: invoice.Amount,
FeeLimit: &feeLimit,
sendPaymentRequest, err := createLnRpcSendRequest(invoice)
if err != nil {
return sendPaymentResponse, err
}
// Execute the payment
sendPaymentResult, err := svc.LndClient.SendPaymentSync(ctx, &sendPaymentRequest)
sendPaymentResult, err := svc.LndClient.SendPaymentSync(ctx, sendPaymentRequest)
if err != nil {
return sendPaymentResponse, err
}
@@ -138,6 +129,44 @@ func (svc *LndhubService) SendPaymentSync(ctx context.Context, invoice *models.I
return sendPaymentResponse, nil
}
func createLnRpcSendRequest(invoice *models.Invoice) (*lnrpc.SendRequest, error) {
// TODO: set dynamic fee limit
feeLimit := lnrpc.FeeLimit{
//Limit: &lnrpc.FeeLimit_Percent{
// Percent: 2,
//},
Limit: &lnrpc.FeeLimit_Fixed{
Fixed: 300,
},
}
if !invoice.Keysend {
return &lnrpc.SendRequest{
PaymentRequest: invoice.PaymentRequest,
Amt: invoice.Amount,
FeeLimit: &feeLimit,
}, nil
}
preImage := makePreimageHex()
pHash := sha256.New()
pHash.Write(preImage)
// Prepare the LNRPC call
//See: https://github.com/hsjoberg/blixt-wallet/blob/9fcc56a7dc25237bc14b85e6490adb9e044c009c/src/lndmobile/index.ts#L251-L270
destBytes, err := hex.DecodeString(invoice.DestinationPubkeyHex)
if err != nil {
return nil, err
}
return &lnrpc.SendRequest{
Dest: destBytes,
Amt: invoice.Amount,
PaymentHash: pHash.Sum(nil),
FeeLimit: &feeLimit,
DestFeatures: []lnrpc.FeatureBit{lnrpc.FeatureBit_TLV_ONION_REQ},
DestCustomRecords: map[uint64][]byte{KEYSEND_CUSTOM_RECORD: preImage, TLV_WHATSAT_MESSAGE: []byte(invoice.Memo)},
}, nil
}
func (svc *LndhubService) PayInvoice(ctx context.Context, invoice *models.Invoice) (*SendPaymentResponse, error) {
userId := invoice.UserID
@@ -275,19 +304,20 @@ func (svc *LndhubService) HandleSuccessfulPayment(ctx context.Context, invoice *
return nil
}
func (svc *LndhubService) AddOutgoingInvoice(ctx context.Context, userID int64, paymentRequest string, decodedInvoice *lnrpc.PayReq) (*models.Invoice, error) {
func (svc *LndhubService) AddOutgoingInvoice(ctx context.Context, userID int64, paymentRequest string, lnPayReq *lnd.LNPayReq) (*models.Invoice, error) {
// Initialize new DB invoice
invoice := models.Invoice{
Type: common.InvoiceTypeOutgoing,
UserID: userID,
PaymentRequest: paymentRequest,
RHash: decodedInvoice.PaymentHash,
Amount: decodedInvoice.NumSatoshis,
RHash: lnPayReq.PayReq.PaymentHash,
Amount: lnPayReq.PayReq.NumSatoshis,
State: common.InvoiceStateInitialized,
DestinationPubkeyHex: decodedInvoice.Destination,
DescriptionHash: decodedInvoice.DescriptionHash,
Memo: decodedInvoice.Description,
ExpiresAt: bun.NullTime{Time: time.Unix(decodedInvoice.Timestamp, 0).Add(time.Duration(decodedInvoice.Expiry) * time.Second)},
DestinationPubkeyHex: lnPayReq.PayReq.Destination,
DescriptionHash: lnPayReq.PayReq.DescriptionHash,
Memo: lnPayReq.PayReq.Description,
Keysend: lnPayReq.Keysend,
ExpiresAt: bun.NullTime{Time: time.Unix(lnPayReq.PayReq.Timestamp, 0).Add(time.Duration(lnPayReq.PayReq.Expiry) * time.Second)},
}
// Save invoice

View File

@@ -6,6 +6,13 @@ import (
"github.com/lightningnetwork/lnd/lnrpc"
)
//https://github.com/hsjoberg/blixt-wallet/blob/9fcc56a7dc25237bc14b85e6490adb9e044c009c/src/utils/constants.ts#L5
const (
KEYSEND_CUSTOM_RECORD = 5482373484
TLV_WHATSAT_MESSAGE = 34349334
TLV_RECORD_NAME = 128100
)
func (svc *LndhubService) GetInfo(ctx context.Context) (*lnrpc.GetInfoResponse, error) {
return svc.LndClient.GetInfo(ctx, &lnrpc.GetInfoRequest{})
}

View File

@@ -15,6 +15,11 @@ import (
"gopkg.in/macaroon.v2"
)
type LNPayReq struct {
PayReq *lnrpc.PayReq
Keysend bool
}
// LNDoptions are the options for the connection to the lnd node.
type LNDoptions struct {
Address string

View File

@@ -134,6 +134,7 @@ func main() {
secured.GET("/checkpayment/:payment_hash", controllers.NewCheckPaymentController(svc).CheckPayment)
secured.GET("/balance", controllers.NewBalanceController(svc).Balance)
secured.GET("/getinfo", controllers.NewGetInfoController(svc).GetInfo)
secured.POST("/keysend", controllers.NewKeySendController(svc).KeySend)
// These endpoints are currently not supported and we return a blank response for backwards compatibility
blankController := controllers.NewBlankController(svc)