mirror of
https://github.com/getAlby/hub.git
synced 2026-08-13 12:33:39 +02:00
Refactor: API cleanup (#338)
This commit is contained in:
parent
a05aafb22b
commit
073d669e03
62 changed files with 3106 additions and 2808 deletions
|
|
@ -4,21 +4,22 @@ import (
|
|||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/getAlby/nostr-wallet-connect/models/api"
|
||||
models "github.com/getAlby/nostr-wallet-connect/models/http"
|
||||
"github.com/getAlby/nostr-wallet-connect/config"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type AlbyHttpService struct {
|
||||
albyOAuthSvc *AlbyOAuthService
|
||||
albyOAuthSvc AlbyOAuthService
|
||||
logger *logrus.Logger
|
||||
appConfig *config.AppConfig
|
||||
}
|
||||
|
||||
func NewAlbyHttpService(albyOAuthSvc *AlbyOAuthService, logger *logrus.Logger) *AlbyHttpService {
|
||||
func NewAlbyHttpService(albyOAuthSvc AlbyOAuthService, logger *logrus.Logger, appConfig *config.AppConfig) *AlbyHttpService {
|
||||
return &AlbyHttpService{
|
||||
albyOAuthSvc: albyOAuthSvc,
|
||||
logger: logger,
|
||||
appConfig: appConfig,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -36,20 +37,20 @@ func (albyHttpSvc *AlbyHttpService) albyCallbackHandler(c echo.Context) error {
|
|||
err := albyHttpSvc.albyOAuthSvc.CallbackHandler(c.Request().Context(), code)
|
||||
if err != nil {
|
||||
albyHttpSvc.logger.WithError(err).Error("Failed to handle Alby OAuth callback")
|
||||
return c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, ErrorResponse{
|
||||
Message: fmt.Sprintf("Failed to handle Alby OAuth callback: %s", err.Error()),
|
||||
})
|
||||
}
|
||||
|
||||
if albyHttpSvc.albyOAuthSvc.appConfig.IsDefaultClientId() {
|
||||
if albyHttpSvc.appConfig.IsDefaultClientId() {
|
||||
// do not redirect if using default OAuth client
|
||||
// redirect will be handled by the frontend instead
|
||||
return c.NoContent(http.StatusNoContent)
|
||||
}
|
||||
|
||||
redirectUrl := albyHttpSvc.albyOAuthSvc.appConfig.FrontendUrl
|
||||
redirectUrl := albyHttpSvc.appConfig.FrontendUrl
|
||||
if redirectUrl == "" {
|
||||
redirectUrl = albyHttpSvc.albyOAuthSvc.appConfig.BaseUrl
|
||||
redirectUrl = albyHttpSvc.appConfig.BaseUrl
|
||||
}
|
||||
|
||||
return c.Redirect(http.StatusFound, redirectUrl)
|
||||
|
|
@ -59,7 +60,7 @@ func (albyHttpSvc *AlbyHttpService) albyMeHandler(c echo.Context) error {
|
|||
me, err := albyHttpSvc.albyOAuthSvc.GetMe(c.Request().Context())
|
||||
if err != nil {
|
||||
albyHttpSvc.logger.WithError(err).Error("Failed to request alby me endpoint")
|
||||
return c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, ErrorResponse{
|
||||
Message: fmt.Sprintf("Failed to request alby me endpoint: %s", err.Error()),
|
||||
})
|
||||
}
|
||||
|
|
@ -71,20 +72,20 @@ func (albyHttpSvc *AlbyHttpService) albyBalanceHandler(c echo.Context) error {
|
|||
balance, err := albyHttpSvc.albyOAuthSvc.GetBalance(c.Request().Context())
|
||||
if err != nil {
|
||||
albyHttpSvc.logger.WithError(err).Error("Failed to request alby balance endpoint")
|
||||
return c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, ErrorResponse{
|
||||
Message: fmt.Sprintf("Failed to request alby balance endpoint: %s", err.Error()),
|
||||
})
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, &api.AlbyBalanceResponse{
|
||||
return c.JSON(http.StatusOK, &AlbyBalanceResponse{
|
||||
Sats: balance.Balance,
|
||||
})
|
||||
}
|
||||
|
||||
func (albyHttpSvc *AlbyHttpService) albyPayHandler(c echo.Context) error {
|
||||
var payRequest api.AlbyPayRequest
|
||||
var payRequest AlbyPayRequest
|
||||
if err := c.Bind(&payRequest); err != nil {
|
||||
return c.JSON(http.StatusBadRequest, models.ErrorResponse{
|
||||
return c.JSON(http.StatusBadRequest, ErrorResponse{
|
||||
Message: fmt.Sprintf("Bad request: %s", err.Error()),
|
||||
})
|
||||
}
|
||||
|
|
@ -92,7 +93,7 @@ func (albyHttpSvc *AlbyHttpService) albyPayHandler(c echo.Context) error {
|
|||
err := albyHttpSvc.albyOAuthSvc.SendPayment(c.Request().Context(), payRequest.Invoice)
|
||||
if err != nil {
|
||||
albyHttpSvc.logger.WithError(err).Error("Failed to request alby pay endpoint")
|
||||
return c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, ErrorResponse{
|
||||
Message: fmt.Sprintf("Failed to request alby pay endpoint: %s", err.Error()),
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,52 +8,24 @@ import (
|
|||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/getAlby/nostr-wallet-connect/config"
|
||||
"github.com/getAlby/nostr-wallet-connect/db"
|
||||
"github.com/getAlby/nostr-wallet-connect/events"
|
||||
"github.com/getAlby/nostr-wallet-connect/models/api"
|
||||
"github.com/getAlby/nostr-wallet-connect/models/config"
|
||||
"github.com/getAlby/nostr-wallet-connect/nip47"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
type AlbyOAuthService struct {
|
||||
type albyOAuthService struct {
|
||||
appConfig *config.AppConfig
|
||||
config config.Config
|
||||
oauthConf *oauth2.Config
|
||||
logger *logrus.Logger
|
||||
api api.API
|
||||
}
|
||||
|
||||
// TODO: move to models/alby
|
||||
type AlbyMe struct {
|
||||
Identifier string `json:"identifier"`
|
||||
NPub string `json:"nostr_pubkey"`
|
||||
LightningAddress string `json:"lightning_address"`
|
||||
Email string `json:"email"`
|
||||
Name string `json:"name"`
|
||||
Avatar string `json:"avatar"`
|
||||
KeysendPubkey string `json:"keysend_pubkey"`
|
||||
SharedNode bool `json:"shared_node"`
|
||||
}
|
||||
|
||||
type AlbyBalance struct {
|
||||
Balance int64 `json:"balance"`
|
||||
Unit string `json:"unit"`
|
||||
Currency string `json:"currency"`
|
||||
}
|
||||
|
||||
type ChannelPeerSuggestion struct {
|
||||
Network string `json:"network"`
|
||||
PaymentMethod string `json:"paymentMethod"`
|
||||
Pubkey string `json:"pubkey"`
|
||||
Host string `json:"host"`
|
||||
MinimumChannelSize uint64 `json:"minimumChannelSize"`
|
||||
Name string `json:"name"`
|
||||
Image string `json:"image"`
|
||||
Lsp string `json:"lsp"`
|
||||
dbSvc db.DBService
|
||||
}
|
||||
|
||||
const (
|
||||
|
|
@ -63,7 +35,7 @@ const (
|
|||
userIdentifierKey = "AlbyUserIdentifier"
|
||||
)
|
||||
|
||||
func NewAlbyOAuthService(logger *logrus.Logger, kvStore config.Config, appConfig *config.AppConfig, api api.API) *AlbyOAuthService {
|
||||
func NewAlbyOAuthService(logger *logrus.Logger, config config.Config, appConfig *config.AppConfig, dbSvc db.DBService) *albyOAuthService {
|
||||
conf := &oauth2.Config{
|
||||
ClientID: appConfig.AlbyClientId,
|
||||
ClientSecret: appConfig.AlbyClientSecret,
|
||||
|
|
@ -81,17 +53,17 @@ func NewAlbyOAuthService(logger *logrus.Logger, kvStore config.Config, appConfig
|
|||
conf.RedirectURL = appConfig.BaseUrl + "/api/alby/callback"
|
||||
}
|
||||
|
||||
albyOAuthSvc := &AlbyOAuthService{
|
||||
albyOAuthSvc := &albyOAuthService{
|
||||
appConfig: appConfig,
|
||||
oauthConf: conf,
|
||||
config: kvStore,
|
||||
config: config,
|
||||
logger: logger,
|
||||
api: api,
|
||||
dbSvc: dbSvc,
|
||||
}
|
||||
return albyOAuthSvc
|
||||
}
|
||||
|
||||
func (svc *AlbyOAuthService) CallbackHandler(ctx context.Context, code string) error {
|
||||
func (svc *albyOAuthService) CallbackHandler(ctx context.Context, code string) error {
|
||||
token, err := svc.oauthConf.Exchange(ctx, code)
|
||||
if err != nil {
|
||||
svc.logger.WithError(err).Error("Failed to exchange token")
|
||||
|
|
@ -122,7 +94,7 @@ func (svc *AlbyOAuthService) CallbackHandler(ctx context.Context, code string) e
|
|||
return nil
|
||||
}
|
||||
|
||||
func (svc *AlbyOAuthService) GetUserIdentifier() (string, error) {
|
||||
func (svc *albyOAuthService) GetUserIdentifier() (string, error) {
|
||||
userIdentifier, err := svc.config.Get(userIdentifierKey, "")
|
||||
if err != nil {
|
||||
svc.logger.WithError(err).Error("Failed to fetch user identifier from user configs")
|
||||
|
|
@ -131,7 +103,7 @@ func (svc *AlbyOAuthService) GetUserIdentifier() (string, error) {
|
|||
return userIdentifier, nil
|
||||
}
|
||||
|
||||
func (svc *AlbyOAuthService) IsConnected(ctx context.Context) bool {
|
||||
func (svc *albyOAuthService) IsConnected(ctx context.Context) bool {
|
||||
token, err := svc.fetchUserToken(ctx)
|
||||
if err != nil {
|
||||
svc.logger.WithError(err).Error("Failed to check fetch token")
|
||||
|
|
@ -139,7 +111,7 @@ func (svc *AlbyOAuthService) IsConnected(ctx context.Context) bool {
|
|||
return token != nil
|
||||
}
|
||||
|
||||
func (svc *AlbyOAuthService) saveToken(token *oauth2.Token) {
|
||||
func (svc *albyOAuthService) saveToken(token *oauth2.Token) {
|
||||
svc.config.SetUpdate(accessTokenExpiryKey, strconv.FormatInt(token.Expiry.Unix(), 10), "")
|
||||
svc.config.SetUpdate(accessTokenKey, token.AccessToken, "")
|
||||
svc.config.SetUpdate(refreshTokenKey, token.RefreshToken, "")
|
||||
|
|
@ -147,7 +119,7 @@ func (svc *AlbyOAuthService) saveToken(token *oauth2.Token) {
|
|||
|
||||
var tokenMutex sync.Mutex
|
||||
|
||||
func (svc *AlbyOAuthService) fetchUserToken(ctx context.Context) (*oauth2.Token, error) {
|
||||
func (svc *albyOAuthService) fetchUserToken(ctx context.Context) (*oauth2.Token, error) {
|
||||
tokenMutex.Lock()
|
||||
defer tokenMutex.Unlock()
|
||||
accessToken, err := svc.config.Get(accessTokenKey, "")
|
||||
|
|
@ -202,8 +174,7 @@ func (svc *AlbyOAuthService) fetchUserToken(ctx context.Context) (*oauth2.Token,
|
|||
return newToken, nil
|
||||
}
|
||||
|
||||
func (svc *AlbyOAuthService) GetMe(ctx context.Context) (*AlbyMe, error) {
|
||||
|
||||
func (svc *albyOAuthService) GetMe(ctx context.Context) (*AlbyMe, error) {
|
||||
token, err := svc.fetchUserToken(ctx)
|
||||
if err != nil {
|
||||
svc.logger.WithError(err).Error("Failed to fetch user token")
|
||||
|
|
@ -237,7 +208,7 @@ func (svc *AlbyOAuthService) GetMe(ctx context.Context) (*AlbyMe, error) {
|
|||
return me, nil
|
||||
}
|
||||
|
||||
func (svc *AlbyOAuthService) GetBalance(ctx context.Context) (*AlbyBalance, error) {
|
||||
func (svc *albyOAuthService) GetBalance(ctx context.Context) (*AlbyBalance, error) {
|
||||
|
||||
token, err := svc.fetchUserToken(ctx)
|
||||
if err != nil {
|
||||
|
|
@ -271,7 +242,7 @@ func (svc *AlbyOAuthService) GetBalance(ctx context.Context) (*AlbyBalance, erro
|
|||
return balance, nil
|
||||
}
|
||||
|
||||
func (svc *AlbyOAuthService) SendPayment(ctx context.Context, invoice string) error {
|
||||
func (svc *albyOAuthService) SendPayment(ctx context.Context, invoice string) error {
|
||||
token, err := svc.fetchUserToken(ctx)
|
||||
if err != nil {
|
||||
svc.logger.WithError(err).Error("Failed to fetch user token")
|
||||
|
|
@ -356,27 +327,28 @@ func (svc *AlbyOAuthService) SendPayment(ctx context.Context, invoice string) er
|
|||
return nil
|
||||
}
|
||||
|
||||
func (svc *AlbyOAuthService) GetAuthUrl() string {
|
||||
func (svc *albyOAuthService) GetAuthUrl() string {
|
||||
if svc.appConfig.AlbyClientId == "" || svc.appConfig.AlbyClientSecret == "" {
|
||||
svc.logger.Fatalf("No ALBY_OAUTH_CLIENT_ID or ALBY_OAUTH_CLIENT_SECRET set")
|
||||
}
|
||||
return svc.oauthConf.AuthCodeURL("unused")
|
||||
}
|
||||
|
||||
func (svc *AlbyOAuthService) LinkAccount(ctx context.Context) error {
|
||||
func (svc *albyOAuthService) LinkAccount(ctx context.Context) error {
|
||||
connectionPubkey, err := svc.createAlbyAccountNWCNode(ctx)
|
||||
if err != nil {
|
||||
svc.logger.WithError(err).Error("Failed to create alby account nwc node")
|
||||
return err
|
||||
}
|
||||
|
||||
app, err := svc.api.CreateApp(&api.CreateAppRequest{
|
||||
Name: "getalby.com",
|
||||
Pubkey: connectionPubkey,
|
||||
MaxAmount: 1_000_000,
|
||||
BudgetRenewal: nip47.BUDGET_RENEWAL_MONTHLY,
|
||||
RequestMethods: nip47.CAPABILITIES,
|
||||
})
|
||||
app, _, err := svc.dbSvc.CreateApp(
|
||||
"getalby.com",
|
||||
connectionPubkey,
|
||||
1_000_000,
|
||||
nip47.BUDGET_RENEWAL_MONTHLY,
|
||||
nil,
|
||||
strings.Split(nip47.CAPABILITIES, " "),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
svc.logger.WithError(err).Error("Failed to create app connection")
|
||||
|
|
@ -396,7 +368,7 @@ func (svc *AlbyOAuthService) LinkAccount(ctx context.Context) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (svc *AlbyOAuthService) ConsumeEvent(ctx context.Context, event *events.Event, globalProperties map[string]interface{}) error {
|
||||
func (svc *albyOAuthService) ConsumeEvent(ctx context.Context, event *events.Event, globalProperties map[string]interface{}) error {
|
||||
// TODO: rename this config option to be specific to the alby API
|
||||
if !svc.appConfig.LogEvents {
|
||||
svc.logger.WithField("event", event).Debug("Skipped sending to alby events API")
|
||||
|
|
@ -481,7 +453,7 @@ func (svc *AlbyOAuthService) ConsumeEvent(ctx context.Context, event *events.Eve
|
|||
return nil
|
||||
}
|
||||
|
||||
func (svc *AlbyOAuthService) createAlbyAccountNWCNode(ctx context.Context) (string, error) {
|
||||
func (svc *albyOAuthService) createAlbyAccountNWCNode(ctx context.Context) (string, error) {
|
||||
token, err := svc.fetchUserToken(ctx)
|
||||
if err != nil {
|
||||
svc.logger.WithError(err).Error("Failed to fetch user token")
|
||||
|
|
@ -548,7 +520,7 @@ func (svc *AlbyOAuthService) createAlbyAccountNWCNode(ctx context.Context) (stri
|
|||
return responsePayload.Pubkey, nil
|
||||
}
|
||||
|
||||
func (svc *AlbyOAuthService) activateAlbyAccountNWCNode(ctx context.Context) error {
|
||||
func (svc *albyOAuthService) activateAlbyAccountNWCNode(ctx context.Context) error {
|
||||
token, err := svc.fetchUserToken(ctx)
|
||||
if err != nil {
|
||||
svc.logger.WithError(err).Error("Failed to fetch user token")
|
||||
|
|
@ -583,7 +555,7 @@ func (svc *AlbyOAuthService) activateAlbyAccountNWCNode(ctx context.Context) err
|
|||
return nil
|
||||
}
|
||||
|
||||
func (svc *AlbyOAuthService) GetChannelPeerSuggestions(ctx context.Context) ([]ChannelPeerSuggestion, error) {
|
||||
func (svc *albyOAuthService) GetChannelPeerSuggestions(ctx context.Context) ([]ChannelPeerSuggestion, error) {
|
||||
|
||||
token, err := svc.fetchUserToken(ctx)
|
||||
if err != nil {
|
||||
60
alby/models.go
Normal file
60
alby/models.go
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
package alby
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/getAlby/nostr-wallet-connect/events"
|
||||
)
|
||||
|
||||
type AlbyOAuthService interface {
|
||||
events.EventSubscriber
|
||||
GetChannelPeerSuggestions(ctx context.Context) ([]ChannelPeerSuggestion, error)
|
||||
GetAuthUrl() string
|
||||
GetUserIdentifier() (string, error)
|
||||
IsConnected(ctx context.Context) bool
|
||||
LinkAccount(ctx context.Context) error
|
||||
CallbackHandler(ctx context.Context, code string) error
|
||||
GetBalance(ctx context.Context) (*AlbyBalance, error)
|
||||
GetMe(ctx context.Context) (*AlbyMe, error)
|
||||
SendPayment(ctx context.Context, invoice string) error
|
||||
}
|
||||
|
||||
type AlbyBalanceResponse struct {
|
||||
Sats int64 `json:"sats"`
|
||||
}
|
||||
|
||||
type AlbyPayRequest struct {
|
||||
Invoice string `json:"invoice"`
|
||||
}
|
||||
|
||||
type AlbyMe struct {
|
||||
Identifier string `json:"identifier"`
|
||||
NPub string `json:"nostr_pubkey"`
|
||||
LightningAddress string `json:"lightning_address"`
|
||||
Email string `json:"email"`
|
||||
Name string `json:"name"`
|
||||
Avatar string `json:"avatar"`
|
||||
KeysendPubkey string `json:"keysend_pubkey"`
|
||||
SharedNode bool `json:"shared_node"`
|
||||
}
|
||||
|
||||
type AlbyBalance struct {
|
||||
Balance int64 `json:"balance"`
|
||||
Unit string `json:"unit"`
|
||||
Currency string `json:"currency"`
|
||||
}
|
||||
|
||||
type ChannelPeerSuggestion struct {
|
||||
Network string `json:"network"`
|
||||
PaymentMethod string `json:"paymentMethod"`
|
||||
Pubkey string `json:"pubkey"`
|
||||
Host string `json:"host"`
|
||||
MinimumChannelSize uint64 `json:"minimumChannelSize"`
|
||||
Name string `json:"name"`
|
||||
Image string `json:"image"`
|
||||
Lsp string `json:"lsp"`
|
||||
}
|
||||
|
||||
type ErrorResponse struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
643
api/api.go
Normal file
643
api/api.go
Normal file
|
|
@ -0,0 +1,643 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/getAlby/nostr-wallet-connect/alby"
|
||||
"github.com/getAlby/nostr-wallet-connect/backup"
|
||||
"github.com/getAlby/nostr-wallet-connect/db"
|
||||
"github.com/getAlby/nostr-wallet-connect/lnclient"
|
||||
"github.com/getAlby/nostr-wallet-connect/lsp"
|
||||
"github.com/getAlby/nostr-wallet-connect/nip47"
|
||||
"github.com/getAlby/nostr-wallet-connect/service"
|
||||
"github.com/getAlby/nostr-wallet-connect/utils"
|
||||
)
|
||||
|
||||
type api struct {
|
||||
logger *logrus.Logger
|
||||
svc service.Service
|
||||
lspSvc lsp.LSPService
|
||||
backupSvc backup.BackupService
|
||||
db *gorm.DB
|
||||
dbSvc db.DBService
|
||||
}
|
||||
|
||||
func NewAPI(svc service.Service, logger *logrus.Logger, gormDb *gorm.DB) *api {
|
||||
|
||||
return &api{
|
||||
svc: svc,
|
||||
logger: logger,
|
||||
db: gormDb,
|
||||
dbSvc: db.NewDBService(gormDb, logger),
|
||||
lspSvc: lsp.NewLSPService(svc, logger),
|
||||
backupSvc: backup.NewBackupService(svc, logger),
|
||||
}
|
||||
}
|
||||
|
||||
func (api *api) CreateApp(createAppRequest *CreateAppRequest) (*CreateAppResponse, error) {
|
||||
expiresAt, err := api.parseExpiresAt(createAppRequest.ExpiresAt)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid expiresAt: %v", err)
|
||||
}
|
||||
|
||||
// request methods are a space separated list of known request kinds TODO: it should be a string array in the API
|
||||
requestMethods := strings.Split(createAppRequest.RequestMethods, " ")
|
||||
if len(requestMethods) == 0 {
|
||||
return nil, fmt.Errorf("won't create an app without request methods")
|
||||
}
|
||||
|
||||
app, pairingSecretKey, err := api.dbSvc.CreateApp(createAppRequest.Name, createAppRequest.Pubkey, createAppRequest.MaxAmount, createAppRequest.BudgetRenewal, expiresAt, requestMethods)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
relayUrl := api.svc.GetConfig().GetRelayUrl()
|
||||
|
||||
responseBody := &CreateAppResponse{}
|
||||
responseBody.Name = createAppRequest.Name
|
||||
responseBody.Pubkey = app.NostrPubkey
|
||||
responseBody.PairingSecret = pairingSecretKey
|
||||
|
||||
if createAppRequest.ReturnTo != "" {
|
||||
returnToUrl, err := url.Parse(createAppRequest.ReturnTo)
|
||||
if err == nil {
|
||||
query := returnToUrl.Query()
|
||||
query.Add("relay", relayUrl)
|
||||
query.Add("pubkey", api.svc.GetConfig().GetNostrPublicKey())
|
||||
// if user.LightningAddress != "" {
|
||||
// query.Add("lud16", user.LightningAddress)
|
||||
// }
|
||||
returnToUrl.RawQuery = query.Encode()
|
||||
responseBody.ReturnTo = returnToUrl.String()
|
||||
}
|
||||
}
|
||||
|
||||
var lud16 string
|
||||
// if user.LightningAddress != "" {
|
||||
// lud16 = fmt.Sprintf("&lud16=%s", user.LightningAddress)
|
||||
// }
|
||||
responseBody.PairingUri = fmt.Sprintf("nostr+walletconnect://%s?relay=%s&secret=%s%s", api.svc.GetConfig().GetNostrPublicKey(), relayUrl, pairingSecretKey, lud16)
|
||||
return responseBody, nil
|
||||
}
|
||||
|
||||
func (api *api) UpdateApp(userApp *db.App, updateAppRequest *UpdateAppRequest) error {
|
||||
maxAmount := updateAppRequest.MaxAmount
|
||||
budgetRenewal := updateAppRequest.BudgetRenewal
|
||||
|
||||
requestMethods := updateAppRequest.RequestMethods
|
||||
if requestMethods == "" {
|
||||
return fmt.Errorf("won't update an app to have no request methods")
|
||||
}
|
||||
newRequestMethods := strings.Split(requestMethods, " ")
|
||||
|
||||
expiresAt, err := api.parseExpiresAt(updateAppRequest.ExpiresAt)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid expiresAt: %v", err)
|
||||
}
|
||||
|
||||
err = api.db.Transaction(func(tx *gorm.DB) error {
|
||||
// Update existing permissions with new budget and expiry
|
||||
err := tx.Model(&db.AppPermission{}).Where("app_id", userApp.ID).Updates(map[string]interface{}{
|
||||
"ExpiresAt": expiresAt,
|
||||
"MaxAmount": maxAmount,
|
||||
"BudgetRenewal": budgetRenewal,
|
||||
}).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var existingPermissions []db.AppPermission
|
||||
if err := tx.Where("app_id = ?", userApp.ID).Find(&existingPermissions).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
existingMethodMap := make(map[string]bool)
|
||||
for _, perm := range existingPermissions {
|
||||
existingMethodMap[perm.RequestMethod] = true
|
||||
}
|
||||
|
||||
// Add new permissions
|
||||
for _, method := range newRequestMethods {
|
||||
if !existingMethodMap[method] {
|
||||
perm := db.AppPermission{
|
||||
App: *userApp,
|
||||
RequestMethod: method,
|
||||
ExpiresAt: expiresAt,
|
||||
MaxAmount: maxAmount,
|
||||
BudgetRenewal: budgetRenewal,
|
||||
}
|
||||
if err := tx.Create(&perm).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
delete(existingMethodMap, method)
|
||||
}
|
||||
|
||||
// Remove old permissions
|
||||
for method := range existingMethodMap {
|
||||
if err := tx.Where("app_id = ? AND request_method = ?", userApp.ID, method).Delete(&db.AppPermission{}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// commit transaction
|
||||
return nil
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (api *api) DeleteApp(userApp *db.App) error {
|
||||
return api.db.Delete(userApp).Error
|
||||
}
|
||||
|
||||
func (api *api) GetApp(userApp *db.App) *App {
|
||||
|
||||
var lastEvent db.RequestEvent
|
||||
lastEventResult := api.db.Where("app_id = ?", userApp.ID).Order("id desc").Limit(1).Find(&lastEvent)
|
||||
|
||||
paySpecificPermission := db.AppPermission{}
|
||||
appPermissions := []db.AppPermission{}
|
||||
var expiresAt *time.Time
|
||||
api.db.Where("app_id = ?", userApp.ID).Find(&appPermissions)
|
||||
|
||||
requestMethods := []string{}
|
||||
for _, appPerm := range appPermissions {
|
||||
expiresAt = appPerm.ExpiresAt
|
||||
if appPerm.RequestMethod == nip47.PAY_INVOICE_METHOD {
|
||||
//find the pay_invoice-specific permissions
|
||||
paySpecificPermission = appPerm
|
||||
}
|
||||
requestMethods = append(requestMethods, appPerm.RequestMethod)
|
||||
}
|
||||
|
||||
//renewsIn := ""
|
||||
budgetUsage := int64(0)
|
||||
maxAmount := paySpecificPermission.MaxAmount
|
||||
if maxAmount > 0 {
|
||||
budgetUsage = api.svc.GetBudgetUsage(&paySpecificPermission)
|
||||
}
|
||||
|
||||
response := App{
|
||||
Name: userApp.Name,
|
||||
Description: userApp.Description,
|
||||
CreatedAt: userApp.CreatedAt,
|
||||
UpdatedAt: userApp.UpdatedAt,
|
||||
NostrPubkey: userApp.NostrPubkey,
|
||||
ExpiresAt: expiresAt,
|
||||
MaxAmount: maxAmount,
|
||||
RequestMethods: requestMethods,
|
||||
BudgetUsage: budgetUsage,
|
||||
BudgetRenewal: paySpecificPermission.BudgetRenewal,
|
||||
}
|
||||
|
||||
if lastEventResult.RowsAffected > 0 {
|
||||
response.LastEventAt = &lastEvent.CreatedAt
|
||||
}
|
||||
|
||||
return &response
|
||||
|
||||
}
|
||||
|
||||
func (api *api) ListApps() ([]App, error) {
|
||||
// TODO: join dbApps and permissions
|
||||
dbApps := []db.App{}
|
||||
api.db.Find(&dbApps)
|
||||
|
||||
permissions := []db.AppPermission{}
|
||||
api.db.Find(&permissions)
|
||||
|
||||
permissionsMap := make(map[uint][]db.AppPermission)
|
||||
for _, perm := range permissions {
|
||||
permissionsMap[perm.AppId] = append(permissionsMap[perm.AppId], perm)
|
||||
}
|
||||
|
||||
apiApps := []App{}
|
||||
for _, userApp := range dbApps {
|
||||
apiApp := App{
|
||||
// ID: app.ID,
|
||||
Name: userApp.Name,
|
||||
Description: userApp.Description,
|
||||
CreatedAt: userApp.CreatedAt,
|
||||
UpdatedAt: userApp.UpdatedAt,
|
||||
NostrPubkey: userApp.NostrPubkey,
|
||||
}
|
||||
|
||||
for _, permission := range permissionsMap[userApp.ID] {
|
||||
apiApp.RequestMethods = append(apiApp.RequestMethods, permission.RequestMethod)
|
||||
apiApp.ExpiresAt = permission.ExpiresAt
|
||||
if permission.RequestMethod == nip47.PAY_INVOICE_METHOD {
|
||||
apiApp.BudgetRenewal = permission.BudgetRenewal
|
||||
apiApp.MaxAmount = permission.MaxAmount
|
||||
if apiApp.MaxAmount > 0 {
|
||||
apiApp.BudgetUsage = api.svc.GetBudgetUsage(&permission)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var lastEvent db.RequestEvent
|
||||
lastEventResult := api.db.Where("app_id = ?", userApp.ID).Order("id desc").Limit(1).Find(&lastEvent)
|
||||
if lastEventResult.RowsAffected > 0 {
|
||||
apiApp.LastEventAt = &lastEvent.CreatedAt
|
||||
}
|
||||
|
||||
apiApps = append(apiApps, apiApp)
|
||||
}
|
||||
return apiApps, nil
|
||||
}
|
||||
|
||||
func (api *api) ListChannels(ctx context.Context) ([]lnclient.Channel, error) {
|
||||
if api.svc.GetLNClient() == nil {
|
||||
return nil, errors.New("LNClient not started")
|
||||
}
|
||||
return api.svc.GetLNClient().ListChannels(ctx)
|
||||
}
|
||||
|
||||
func (api *api) GetChannelPeerSuggestions(ctx context.Context) ([]alby.ChannelPeerSuggestion, error) {
|
||||
return api.svc.GetAlbyOAuthSvc().GetChannelPeerSuggestions(ctx)
|
||||
}
|
||||
|
||||
func (api *api) ResetRouter(key string) error {
|
||||
if api.svc.GetLNClient() == nil {
|
||||
return errors.New("LNClient not started")
|
||||
}
|
||||
err := api.svc.GetLNClient().ResetRouter(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Because the above method has to stop the node to reset the router,
|
||||
// We also need to stop the lnclient and ask the user to start it again
|
||||
return api.Stop()
|
||||
}
|
||||
|
||||
func (api *api) ChangeUnlockPassword(changeUnlockPasswordRequest *ChangeUnlockPasswordRequest) error {
|
||||
if api.svc.GetLNClient() == nil {
|
||||
return errors.New("LNClient not started")
|
||||
}
|
||||
|
||||
err := api.svc.GetConfig().ChangeUnlockPassword(changeUnlockPasswordRequest.CurrentUnlockPassword, changeUnlockPasswordRequest.NewUnlockPassword)
|
||||
|
||||
if err != nil {
|
||||
api.logger.WithError(err).Error("failed to change unlock password")
|
||||
return err
|
||||
}
|
||||
|
||||
// Because all the encrypted fields have changed
|
||||
// we also need to stop the lnclient and ask the user to start it again
|
||||
return api.Stop()
|
||||
}
|
||||
|
||||
func (api *api) Stop() error {
|
||||
api.logger.Info("Running Stop command")
|
||||
if api.svc.GetLNClient() == nil {
|
||||
return errors.New("LNClient not started")
|
||||
}
|
||||
// stop the lnclient
|
||||
// The user will be forced to re-enter their unlock password to restart the node
|
||||
err := api.svc.StopLNClient()
|
||||
if err != nil {
|
||||
api.logger.WithError(err).Error("Failed to stop LNClient")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (api *api) GetNodeConnectionInfo(ctx context.Context) (*lnclient.NodeConnectionInfo, error) {
|
||||
if api.svc.GetLNClient() == nil {
|
||||
return nil, errors.New("LNClient not started")
|
||||
}
|
||||
return api.svc.GetLNClient().GetNodeConnectionInfo(ctx)
|
||||
}
|
||||
|
||||
func (api *api) GetNodeStatus(ctx context.Context) (*lnclient.NodeStatus, error) {
|
||||
if api.svc.GetLNClient() == nil {
|
||||
return nil, errors.New("LNClient not started")
|
||||
}
|
||||
return api.svc.GetLNClient().GetNodeStatus(ctx)
|
||||
}
|
||||
|
||||
func (api *api) ListPeers(ctx context.Context) ([]lnclient.PeerDetails, error) {
|
||||
if api.svc.GetLNClient() == nil {
|
||||
return nil, errors.New("LNClient not started")
|
||||
}
|
||||
return api.svc.GetLNClient().ListPeers(ctx)
|
||||
}
|
||||
|
||||
func (api *api) ConnectPeer(ctx context.Context, connectPeerRequest *ConnectPeerRequest) error {
|
||||
if api.svc.GetLNClient() == nil {
|
||||
return errors.New("LNClient not started")
|
||||
}
|
||||
return api.svc.GetLNClient().ConnectPeer(ctx, connectPeerRequest)
|
||||
}
|
||||
|
||||
func (api *api) OpenChannel(ctx context.Context, openChannelRequest *OpenChannelRequest) (*OpenChannelResponse, error) {
|
||||
if api.svc.GetLNClient() == nil {
|
||||
return nil, errors.New("LNClient not started")
|
||||
}
|
||||
return api.svc.GetLNClient().OpenChannel(ctx, openChannelRequest)
|
||||
}
|
||||
|
||||
func (api *api) CloseChannel(ctx context.Context, peerId, channelId string, force bool) (*CloseChannelResponse, error) {
|
||||
if api.svc.GetLNClient() == nil {
|
||||
return nil, errors.New("LNClient not started")
|
||||
}
|
||||
api.logger.WithFields(logrus.Fields{
|
||||
"peer_id": peerId,
|
||||
"channel_id": channelId,
|
||||
"force": force,
|
||||
}).Info("Closing channel")
|
||||
return api.svc.GetLNClient().CloseChannel(ctx, &lnclient.CloseChannelRequest{
|
||||
NodeId: peerId,
|
||||
ChannelId: channelId,
|
||||
Force: force,
|
||||
})
|
||||
}
|
||||
|
||||
func (api *api) GetNewOnchainAddress(ctx context.Context) (*NewOnchainAddressResponse, error) {
|
||||
if api.svc.GetLNClient() == nil {
|
||||
return nil, errors.New("LNClient not started")
|
||||
}
|
||||
address, err := api.svc.GetLNClient().GetNewOnchainAddress(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &NewOnchainAddressResponse{
|
||||
Address: address,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (api *api) SignMessage(ctx context.Context, message string) (*SignMessageResponse, error) {
|
||||
if api.svc.GetLNClient() == nil {
|
||||
return nil, errors.New("LNClient not started")
|
||||
}
|
||||
signature, err := api.svc.GetLNClient().SignMessage(ctx, message)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &SignMessageResponse{
|
||||
Message: message,
|
||||
Signature: signature,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (api *api) RedeemOnchainFunds(ctx context.Context, toAddress string) (*RedeemOnchainFundsResponse, error) {
|
||||
if api.svc.GetLNClient() == nil {
|
||||
return nil, errors.New("LNClient not started")
|
||||
}
|
||||
txId, err := api.svc.GetLNClient().RedeemOnchainFunds(ctx, toAddress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &RedeemOnchainFundsResponse{
|
||||
TxId: txId,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (api *api) GetBalances(ctx context.Context) (*BalancesResponse, error) {
|
||||
if api.svc.GetLNClient() == nil {
|
||||
return nil, errors.New("LNClient not started")
|
||||
}
|
||||
balances, err := api.svc.GetLNClient().GetBalances(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return balances, nil
|
||||
}
|
||||
|
||||
func (api *api) RequestMempoolApi(endpoint string) (interface{}, error) {
|
||||
url := api.svc.GetConfig().GetEnv().MempoolApi + endpoint
|
||||
|
||||
client := http.Client{
|
||||
Timeout: time.Second * 10,
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
api.logger.WithError(err).WithFields(logrus.Fields{
|
||||
"url": url,
|
||||
}).Error("Failed to create http request")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res, err := client.Do(req)
|
||||
if err != nil {
|
||||
api.logger.WithError(err).WithFields(logrus.Fields{
|
||||
"url": url,
|
||||
}).Error("Failed to send request")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer res.Body.Close()
|
||||
|
||||
body, readErr := io.ReadAll(res.Body)
|
||||
if readErr != nil {
|
||||
api.logger.WithError(err).WithFields(logrus.Fields{
|
||||
"url": url,
|
||||
}).Error("Failed to read response body")
|
||||
return nil, errors.New("failed to read response body")
|
||||
}
|
||||
|
||||
var jsonContent interface{}
|
||||
jsonErr := json.Unmarshal(body, &jsonContent)
|
||||
if jsonErr != nil {
|
||||
api.logger.WithError(jsonErr).WithFields(logrus.Fields{
|
||||
"url": url,
|
||||
}).Error("Failed to deserialize json")
|
||||
return nil, fmt.Errorf("failed to deserialize json %s %s", url, string(body))
|
||||
}
|
||||
return jsonContent, nil
|
||||
}
|
||||
|
||||
func (api *api) GetInfo(ctx context.Context) (*InfoResponse, error) {
|
||||
info := InfoResponse{}
|
||||
backendType, _ := api.svc.GetConfig().Get("LNBackendType", "")
|
||||
unlockPasswordCheck, _ := api.svc.GetConfig().Get("UnlockPasswordCheck", "")
|
||||
info.SetupCompleted = unlockPasswordCheck != ""
|
||||
info.Running = api.svc.GetLNClient() != nil
|
||||
info.BackendType = backendType
|
||||
info.AlbyAuthUrl = api.svc.GetAlbyOAuthSvc().GetAuthUrl()
|
||||
info.OAuthRedirect = !api.svc.GetConfig().GetEnv().IsDefaultClientId()
|
||||
albyUserIdentifier, err := api.svc.GetAlbyOAuthSvc().GetUserIdentifier()
|
||||
if err != nil {
|
||||
api.logger.WithError(err).Error("Failed to get alby user identifier")
|
||||
return nil, err
|
||||
}
|
||||
info.AlbyUserIdentifier = albyUserIdentifier
|
||||
info.AlbyAccountConnected = api.svc.GetAlbyOAuthSvc().IsConnected(ctx)
|
||||
if api.svc.GetLNClient() != nil {
|
||||
nodeInfo, err := api.svc.GetLNClient().GetInfo(ctx)
|
||||
if err != nil {
|
||||
api.logger.WithError(err).Error("Failed to get nodeInfo")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info.Network = nodeInfo.Network
|
||||
}
|
||||
|
||||
info.NextBackupReminder, _ = api.svc.GetConfig().Get("NextBackupReminder", "")
|
||||
|
||||
return &info, nil
|
||||
}
|
||||
|
||||
func (api *api) GetEncryptedMnemonic() *EncryptedMnemonicResponse {
|
||||
resp := EncryptedMnemonicResponse{}
|
||||
mnemonic, _ := api.svc.GetConfig().Get("Mnemonic", "")
|
||||
resp.Mnemonic = mnemonic
|
||||
return &resp
|
||||
}
|
||||
|
||||
func (api *api) SetNextBackupReminder(backupReminderRequest *BackupReminderRequest) error {
|
||||
api.svc.GetConfig().SetUpdate("NextBackupReminder", backupReminderRequest.NextBackupReminder, "")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (api *api) Start(startRequest *StartRequest) error {
|
||||
return api.svc.StartApp(startRequest.UnlockPassword)
|
||||
}
|
||||
|
||||
func (api *api) Setup(ctx context.Context, setupRequest *SetupRequest) error {
|
||||
info, err := api.GetInfo(ctx)
|
||||
if err != nil {
|
||||
api.logger.WithError(err).Error("Failed to get info")
|
||||
return err
|
||||
}
|
||||
if info.SetupCompleted {
|
||||
api.logger.Error("Cannot re-setup node")
|
||||
return errors.New("setup already completed")
|
||||
}
|
||||
|
||||
api.svc.GetConfig().Setup(setupRequest.UnlockPassword)
|
||||
|
||||
// TODO: move all below code to cfg.Setup()
|
||||
|
||||
// update next backup reminder
|
||||
api.svc.GetConfig().SetUpdate("NextBackupReminder", setupRequest.NextBackupReminder, "")
|
||||
// only update non-empty values
|
||||
if setupRequest.LNBackendType != "" {
|
||||
api.svc.GetConfig().SetUpdate("LNBackendType", setupRequest.LNBackendType, "")
|
||||
}
|
||||
if setupRequest.BreezAPIKey != "" {
|
||||
api.svc.GetConfig().SetUpdate("BreezAPIKey", setupRequest.BreezAPIKey, setupRequest.UnlockPassword)
|
||||
}
|
||||
if setupRequest.Mnemonic != "" {
|
||||
api.svc.GetConfig().SetUpdate("Mnemonic", setupRequest.Mnemonic, setupRequest.UnlockPassword)
|
||||
}
|
||||
if setupRequest.GreenlightInviteCode != "" {
|
||||
api.svc.GetConfig().SetUpdate("GreenlightInviteCode", setupRequest.GreenlightInviteCode, setupRequest.UnlockPassword)
|
||||
}
|
||||
if setupRequest.LNDAddress != "" {
|
||||
api.svc.GetConfig().SetUpdate("LNDAddress", setupRequest.LNDAddress, setupRequest.UnlockPassword)
|
||||
}
|
||||
if setupRequest.LNDCertHex != "" {
|
||||
api.svc.GetConfig().SetUpdate("LNDCertHex", setupRequest.LNDCertHex, setupRequest.UnlockPassword)
|
||||
}
|
||||
if setupRequest.LNDMacaroonHex != "" {
|
||||
api.svc.GetConfig().SetUpdate("LNDMacaroonHex", setupRequest.LNDMacaroonHex, setupRequest.UnlockPassword)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (api *api) SendPaymentProbes(ctx context.Context, sendPaymentProbesRequest *SendPaymentProbesRequest) (*SendPaymentProbesResponse, error) {
|
||||
if api.svc.GetLNClient() == nil {
|
||||
return nil, errors.New("LNClient not started")
|
||||
}
|
||||
|
||||
var errMessage string
|
||||
err := api.svc.GetLNClient().SendPaymentProbes(ctx, sendPaymentProbesRequest.Invoice)
|
||||
if err != nil {
|
||||
errMessage = err.Error()
|
||||
}
|
||||
|
||||
return &SendPaymentProbesResponse{Error: errMessage}, nil
|
||||
}
|
||||
|
||||
func (api *api) SendSpontaneousPaymentProbes(ctx context.Context, sendSpontaneousPaymentProbesRequest *SendSpontaneousPaymentProbesRequest) (*SendSpontaneousPaymentProbesResponse, error) {
|
||||
if api.svc.GetLNClient() == nil {
|
||||
return nil, errors.New("LNClient not started")
|
||||
}
|
||||
|
||||
var errMessage string
|
||||
err := api.svc.GetLNClient().SendSpontaneousPaymentProbes(ctx, sendSpontaneousPaymentProbesRequest.Amount, sendSpontaneousPaymentProbesRequest.NodeId)
|
||||
if err != nil {
|
||||
errMessage = err.Error()
|
||||
}
|
||||
|
||||
return &SendSpontaneousPaymentProbesResponse{Error: errMessage}, nil
|
||||
}
|
||||
|
||||
func (api *api) GetNetworkGraph(nodeIds []string) (NetworkGraphResponse, error) {
|
||||
if api.svc.GetLNClient() == nil {
|
||||
return nil, errors.New("LNClient not started")
|
||||
}
|
||||
return api.svc.GetLNClient().GetNetworkGraph(nodeIds)
|
||||
}
|
||||
|
||||
func (api *api) SyncWallet() error {
|
||||
if api.svc.GetLNClient() == nil {
|
||||
return errors.New("LNClient not started")
|
||||
}
|
||||
api.svc.GetLNClient().UpdateLastWalletSyncRequest()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (api *api) GetLogOutput(ctx context.Context, logType string, getLogRequest *GetLogOutputRequest) (*GetLogOutputResponse, error) {
|
||||
var err error
|
||||
var logData []byte
|
||||
|
||||
if logType == LogTypeNode {
|
||||
if api.svc.GetLNClient() == nil {
|
||||
return nil, errors.New("LNClient not started")
|
||||
}
|
||||
|
||||
logData, err = api.svc.GetLNClient().GetLogOutput(ctx, getLogRequest.MaxLen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if logType == LogTypeApp {
|
||||
logFileName := api.svc.GetLogFilePath()
|
||||
|
||||
logData, err = utils.ReadFileTail(logFileName, getLogRequest.MaxLen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
return nil, fmt.Errorf("invalid log type: '%s'", logType)
|
||||
}
|
||||
|
||||
return &GetLogOutputResponse{Log: string(logData)}, nil
|
||||
}
|
||||
|
||||
func (api *api) parseExpiresAt(expiresAtString string) (*time.Time, error) {
|
||||
var expiresAt *time.Time
|
||||
if expiresAtString != "" {
|
||||
var err error
|
||||
expiresAtValue, err := time.Parse(time.RFC3339, expiresAtString)
|
||||
if err != nil {
|
||||
api.logger.WithField("expiresAt", expiresAtString).Error("Invalid expiresAt")
|
||||
return nil, fmt.Errorf("invalid expiresAt: %v", err)
|
||||
}
|
||||
expiresAtValue = time.Date(expiresAtValue.Year(), expiresAtValue.Month(), expiresAtValue.Day(), 23, 59, 59, 0, expiresAtValue.Location())
|
||||
expiresAt = &expiresAtValue
|
||||
}
|
||||
return expiresAt, nil
|
||||
}
|
||||
|
||||
func (api *api) GetLSPService() lsp.LSPService {
|
||||
return api.lspSvc
|
||||
}
|
||||
func (api *api) GetBackupService() backup.BackupService {
|
||||
return api.backupSvc
|
||||
}
|
||||
|
|
@ -1,14 +1,50 @@
|
|||
// TODO: move to api/models.go
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/getAlby/nostr-wallet-connect/models/lnclient"
|
||||
"github.com/getAlby/nostr-wallet-connect/alby"
|
||||
"github.com/getAlby/nostr-wallet-connect/backup"
|
||||
"github.com/getAlby/nostr-wallet-connect/db"
|
||||
"github.com/getAlby/nostr-wallet-connect/lnclient"
|
||||
"github.com/getAlby/nostr-wallet-connect/lsp"
|
||||
)
|
||||
|
||||
type API interface {
|
||||
CreateApp(createAppRequest *CreateAppRequest) (*CreateAppResponse, error)
|
||||
UpdateApp(userApp *db.App, updateAppRequest *UpdateAppRequest) error
|
||||
DeleteApp(userApp *db.App) error
|
||||
GetApp(userApp *db.App) *App
|
||||
ListApps() ([]App, error)
|
||||
ListChannels(ctx context.Context) ([]lnclient.Channel, error)
|
||||
GetChannelPeerSuggestions(ctx context.Context) ([]alby.ChannelPeerSuggestion, error)
|
||||
ResetRouter(key string) error
|
||||
ChangeUnlockPassword(changeUnlockPasswordRequest *ChangeUnlockPasswordRequest) error
|
||||
Stop() error
|
||||
GetNodeConnectionInfo(ctx context.Context) (*lnclient.NodeConnectionInfo, error)
|
||||
GetNodeStatus(ctx context.Context) (*lnclient.NodeStatus, error)
|
||||
ListPeers(ctx context.Context) ([]lnclient.PeerDetails, error)
|
||||
ConnectPeer(ctx context.Context, connectPeerRequest *ConnectPeerRequest) error
|
||||
OpenChannel(ctx context.Context, openChannelRequest *OpenChannelRequest) (*OpenChannelResponse, error)
|
||||
CloseChannel(ctx context.Context, peerId, channelId string, force bool) (*CloseChannelResponse, error)
|
||||
GetNewOnchainAddress(ctx context.Context) (*NewOnchainAddressResponse, error)
|
||||
SignMessage(ctx context.Context, message string) (*SignMessageResponse, error)
|
||||
RedeemOnchainFunds(ctx context.Context, toAddress string) (*RedeemOnchainFundsResponse, error)
|
||||
GetBalances(ctx context.Context) (*BalancesResponse, error)
|
||||
RequestMempoolApi(endpoint string) (interface{}, error)
|
||||
GetInfo(ctx context.Context) (*InfoResponse, error)
|
||||
GetEncryptedMnemonic() *EncryptedMnemonicResponse
|
||||
SetNextBackupReminder(backupReminderRequest *BackupReminderRequest) error
|
||||
Start(startRequest *StartRequest) error
|
||||
Setup(ctx context.Context, setupRequest *SetupRequest) error
|
||||
SendPaymentProbes(ctx context.Context, sendPaymentProbesRequest *SendPaymentProbesRequest) (*SendPaymentProbesResponse, error)
|
||||
SendSpontaneousPaymentProbes(ctx context.Context, sendSpontaneousPaymentProbesRequest *SendSpontaneousPaymentProbesRequest) (*SendSpontaneousPaymentProbesResponse, error)
|
||||
GetNetworkGraph(nodeIds []string) (NetworkGraphResponse, error)
|
||||
SyncWallet() error
|
||||
GetLogOutput(ctx context.Context, logType string, getLogRequest *GetLogOutputRequest) (*GetLogOutputResponse, error)
|
||||
GetLSPService() lsp.LSPService
|
||||
GetBackupService() backup.BackupService
|
||||
}
|
||||
|
||||
type App struct {
|
||||
|
|
@ -96,11 +132,10 @@ type InfoResponse struct {
|
|||
BackendType string `json:"backendType"`
|
||||
SetupCompleted bool `json:"setupCompleted"`
|
||||
OAuthRedirect bool `json:"oauthRedirect"`
|
||||
OnboardingCompleted bool `json:"onboardingCompleted"` // TODO: rename - HasChannel?
|
||||
Running bool `json:"running"`
|
||||
Unlocked bool `json:"unlocked"`
|
||||
AlbyAuthUrl string `json:"albyAuthUrl"`
|
||||
ShowBackupReminder bool `json:"showBackupReminder"`
|
||||
NextBackupReminder string `json:"nextBackupReminder"`
|
||||
AlbyUserIdentifier string `json:"albyUserIdentifier"`
|
||||
AlbyAccountConnected bool `json:"albyAccountConnected"`
|
||||
Network string `json:"network"`
|
||||
|
|
@ -120,16 +155,6 @@ type OpenChannelRequest = lnclient.OpenChannelRequest
|
|||
type OpenChannelResponse = lnclient.OpenChannelResponse
|
||||
type CloseChannelResponse = lnclient.CloseChannelResponse
|
||||
|
||||
type NewInstantChannelInvoiceRequest struct {
|
||||
Amount uint64 `json:"amount"`
|
||||
LSP string `json:"lsp"`
|
||||
}
|
||||
|
||||
type NewInstantChannelInvoiceResponse struct {
|
||||
Invoice string `json:"invoice"`
|
||||
Fee uint64 `json:"fee"`
|
||||
}
|
||||
|
||||
type RedeemOnchainFundsRequest struct {
|
||||
ToAddress string `json:"toAddress"`
|
||||
}
|
||||
|
|
@ -185,15 +210,6 @@ type SignMessageResponse struct {
|
|||
Signature string `json:"signature"`
|
||||
}
|
||||
|
||||
// TODO: move to different file
|
||||
type AlbyBalanceResponse struct {
|
||||
Sats int64 `json:"sats"`
|
||||
}
|
||||
|
||||
type AlbyPayRequest struct {
|
||||
Invoice string `json:"invoice"`
|
||||
}
|
||||
|
||||
type ResetRouterRequest struct {
|
||||
Key string `json:"key"`
|
||||
}
|
||||
295
backup/backup_service.go
Normal file
295
backup/backup_service.go
Normal file
|
|
@ -0,0 +1,295 @@
|
|||
package backup
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"archive/zip"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
|
||||
"github.com/getAlby/nostr-wallet-connect/service"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
)
|
||||
|
||||
type backupService struct {
|
||||
svc service.Service
|
||||
logger *logrus.Logger
|
||||
}
|
||||
|
||||
func NewBackupService(svc service.Service, logger *logrus.Logger) *backupService {
|
||||
return &backupService{
|
||||
svc: svc,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
func (bs *backupService) CreateBackup(unlockPassword string, w io.Writer) error {
|
||||
var err error
|
||||
|
||||
if !bs.svc.GetConfig().CheckUnlockPassword(unlockPassword) {
|
||||
return errors.New("invalid unlock password")
|
||||
}
|
||||
|
||||
workDir, err := filepath.Abs(bs.svc.GetConfig().GetEnv().Workdir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get absolute workdir: %w", err)
|
||||
}
|
||||
|
||||
lnStorageDir := ""
|
||||
|
||||
if bs.svc.GetLNClient() == nil {
|
||||
return fmt.Errorf("node not running")
|
||||
}
|
||||
lnStorageDir, err = bs.svc.GetLNClient().GetStorageDir()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get storage dir: %w", err)
|
||||
}
|
||||
bs.logger.WithField("path", lnStorageDir).Info("Found node storage dir")
|
||||
|
||||
// Reset the routing data to decrease the LDK DB size
|
||||
err = bs.svc.GetLNClient().ResetRouter("ALL")
|
||||
if err != nil {
|
||||
bs.logger.WithError(err).Error("Failed to reset router")
|
||||
return fmt.Errorf("failed to reset router: %w", err)
|
||||
}
|
||||
// Stop the app to ensure no new requests are processed.
|
||||
bs.svc.StopApp()
|
||||
|
||||
// Closing the database leaves the service in an inconsistent state,
|
||||
// but that should not be a problem since the app is not expected
|
||||
// to be used after its data is exported.
|
||||
err = bs.svc.StopDb()
|
||||
if err != nil {
|
||||
bs.logger.WithError(err).Error("Failed to stop database")
|
||||
return fmt.Errorf("failed to close database: %w", err)
|
||||
}
|
||||
|
||||
var filesToArchive []string
|
||||
|
||||
if lnStorageDir != "" {
|
||||
lnFiles, err := filepath.Glob(filepath.Join(workDir, lnStorageDir, "*"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to list files in the LNClient storage directory: %w", err)
|
||||
}
|
||||
bs.logger.WithField("lnFiles", lnFiles).Info("Listed node storage dir")
|
||||
|
||||
// Avoid backing up log files.
|
||||
slices.DeleteFunc(lnFiles, func(s string) bool {
|
||||
return filepath.Ext(s) == ".log"
|
||||
})
|
||||
|
||||
filesToArchive = append(filesToArchive, lnFiles...)
|
||||
}
|
||||
|
||||
cw, err := encryptingWriter(w, unlockPassword)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create encrypted writer: %w", err)
|
||||
}
|
||||
|
||||
zw := zip.NewWriter(cw)
|
||||
defer zw.Close()
|
||||
|
||||
addFileToZip := func(fsPath, zipPath string) error {
|
||||
inF, err := os.Open(fsPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open source file for reading: %w", err)
|
||||
}
|
||||
defer inF.Close()
|
||||
|
||||
outW, err := zw.Create(zipPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create zip entry: %w", err)
|
||||
}
|
||||
|
||||
_, err = io.Copy(outW, inF)
|
||||
return err
|
||||
}
|
||||
|
||||
// Locate the main database file.
|
||||
dbFilePath := bs.svc.GetConfig().GetEnv().DatabaseUri
|
||||
// Add the database file to the archive.
|
||||
bs.logger.WithField("nwc.db", dbFilePath).Info("adding nwc db to zip")
|
||||
err = addFileToZip(dbFilePath, "nwc.db")
|
||||
if err != nil {
|
||||
bs.logger.WithError(err).Error("Failed to zip nwc db")
|
||||
return fmt.Errorf("failed to write nwc db file to zip: %w", err)
|
||||
}
|
||||
|
||||
for _, fileToArchive := range filesToArchive {
|
||||
bs.logger.WithField("fileToArchive", fileToArchive).Info("adding file to zip")
|
||||
relPath, err := filepath.Rel(workDir, fileToArchive)
|
||||
if err != nil {
|
||||
bs.logger.WithError(err).Error("Failed to get relative path of input file")
|
||||
return fmt.Errorf("failed to get relative path of input file: %w", err)
|
||||
}
|
||||
|
||||
// Ensure forward slashes for zip format compatibility.
|
||||
err = addFileToZip(fileToArchive, filepath.ToSlash(relPath))
|
||||
if err != nil {
|
||||
bs.logger.WithError(err).Error("Failed to write file to zip")
|
||||
return fmt.Errorf("failed to write input file to zip: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bs *backupService) RestoreBackup(unlockPassword string, r io.Reader) error {
|
||||
workDir, err := filepath.Abs(bs.svc.GetConfig().GetEnv().Workdir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get absolute workdir: %w", err)
|
||||
}
|
||||
|
||||
if strings.HasPrefix(bs.svc.GetConfig().GetEnv().DatabaseUri, "file:") {
|
||||
return errors.New("cannot restore backup when database path is a file URI")
|
||||
}
|
||||
|
||||
cr, err := decryptingReader(r, unlockPassword)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create decrypted reader: %w", err)
|
||||
}
|
||||
|
||||
tmpF, err := os.CreateTemp("", "nwc-*.bkp")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create temporary output file: %w", err)
|
||||
}
|
||||
tmpName := tmpF.Name()
|
||||
defer os.Remove(tmpName)
|
||||
defer tmpF.Close()
|
||||
|
||||
zipSize, err := io.Copy(tmpF, cr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to decrypt backup data into temporary file: %w", err)
|
||||
}
|
||||
|
||||
if err = tmpF.Sync(); err != nil {
|
||||
return fmt.Errorf("failed to flush temporary file: %w", err)
|
||||
}
|
||||
|
||||
if _, err = tmpF.Seek(0, 0); err != nil {
|
||||
return fmt.Errorf("failed to seek to beginning of temporary file: %w", err)
|
||||
}
|
||||
|
||||
zr, err := zip.NewReader(tmpF, zipSize)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create zip reader: %w", err)
|
||||
}
|
||||
|
||||
extractZipEntry := func(zipFile *zip.File) error {
|
||||
fsFilePath := filepath.Join(workDir, "restore", filepath.FromSlash(zipFile.Name))
|
||||
|
||||
if err = os.MkdirAll(filepath.Dir(fsFilePath), 0700); err != nil {
|
||||
return fmt.Errorf("failed to create directory for zip entry: %w", err)
|
||||
}
|
||||
|
||||
inF, err := zipFile.Open()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open zip entry for reading: %w", err)
|
||||
}
|
||||
defer inF.Close()
|
||||
|
||||
outF, err := os.OpenFile(fsFilePath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create destination file: %w", err)
|
||||
}
|
||||
defer outF.Close()
|
||||
|
||||
if _, err = io.Copy(outF, inF); err != nil {
|
||||
return fmt.Errorf("failed to write zip entry to destination file: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
bs.logger.WithField("count", len(zr.File)).Info("Extracting files")
|
||||
for _, f := range zr.File {
|
||||
bs.logger.WithField("file", f.Name).Info("Extracting file")
|
||||
if err = extractZipEntry(f); err != nil {
|
||||
return fmt.Errorf("failed to extract zip entry: %w", err)
|
||||
}
|
||||
}
|
||||
bs.logger.WithField("count", len(zr.File)).Info("Extracted files")
|
||||
|
||||
go func() {
|
||||
bs.logger.Info("Backup restored. Shutting down Alby Hub...")
|
||||
// schedule node shutdown after a few seconds to ensure frontend updates
|
||||
time.Sleep(5 * time.Second)
|
||||
os.Exit(0)
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func encryptingWriter(w io.Writer, password string) (io.Writer, error) {
|
||||
salt := make([]byte, 8)
|
||||
if _, err := rand.Read(salt); err != nil {
|
||||
return nil, fmt.Errorf("failed to generate salt: %w", err)
|
||||
}
|
||||
|
||||
encKey := pbkdf2.Key([]byte(password), salt, 4096, 32, sha256.New)
|
||||
block, err := aes.NewCipher(encKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create AES cipher: %w", err)
|
||||
}
|
||||
|
||||
iv := make([]byte, aes.BlockSize)
|
||||
if _, err = rand.Read(iv); err != nil {
|
||||
return nil, fmt.Errorf("failed to generate IV: %w", err)
|
||||
}
|
||||
|
||||
_, err = w.Write(salt)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to write salt: %w", err)
|
||||
}
|
||||
|
||||
_, err = w.Write(iv)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to write IV: %w", err)
|
||||
}
|
||||
|
||||
stream := cipher.NewOFB(block, iv)
|
||||
cw := &cipher.StreamWriter{
|
||||
S: stream,
|
||||
W: w,
|
||||
}
|
||||
|
||||
return cw, nil
|
||||
}
|
||||
|
||||
func decryptingReader(r io.Reader, password string) (io.Reader, error) {
|
||||
salt := make([]byte, 8)
|
||||
if _, err := io.ReadFull(r, salt); err != nil {
|
||||
return nil, fmt.Errorf("failed to read salt: %w", err)
|
||||
}
|
||||
|
||||
iv := make([]byte, aes.BlockSize)
|
||||
if _, err := io.ReadFull(r, iv); err != nil {
|
||||
return nil, fmt.Errorf("failed to read IV: %w", err)
|
||||
}
|
||||
|
||||
encKey := pbkdf2.Key([]byte(password), salt, 4096, 32, sha256.New)
|
||||
block, err := aes.NewCipher(encKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create AES cipher: %w", err)
|
||||
}
|
||||
|
||||
stream := cipher.NewOFB(block, iv)
|
||||
cr := &cipher.StreamReader{
|
||||
S: stream,
|
||||
R: r,
|
||||
}
|
||||
|
||||
return cr, nil
|
||||
}
|
||||
10
backup/models.go
Normal file
10
backup/models.go
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
package backup
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
type BackupService interface {
|
||||
CreateBackup(unlockPassword string, w io.Writer) error
|
||||
RestoreBackup(unlockPassword string, r io.Reader) error
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package config
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package config
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
|
|
@ -7,16 +7,16 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/nbd-wtf/go-nostr"
|
||||
"github.com/sirupsen/logrus"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
|
||||
"github.com/getAlby/nostr-wallet-connect/models/config"
|
||||
dbModels "github.com/getAlby/nostr-wallet-connect/models/db"
|
||||
dbModels "github.com/getAlby/nostr-wallet-connect/db"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Env *config.AppConfig
|
||||
type config struct {
|
||||
Env *AppConfig
|
||||
CookieSecret string
|
||||
NostrSecretKey string
|
||||
NostrPublicKey string
|
||||
|
|
@ -28,7 +28,13 @@ const (
|
|||
unlockPasswordCheck = "THIS STRING SHOULD MATCH IF PASSWORD IS CORRECT"
|
||||
)
|
||||
|
||||
func (cfg *Config) Init(db *gorm.DB, env *config.AppConfig, logger *logrus.Logger) {
|
||||
func NewConfig(db *gorm.DB, env *AppConfig, logger *logrus.Logger) *config {
|
||||
cfg := &config{}
|
||||
cfg.init(db, env, logger)
|
||||
return cfg
|
||||
}
|
||||
|
||||
func (cfg *config) init(db *gorm.DB, env *AppConfig, logger *logrus.Logger) {
|
||||
cfg.db = db
|
||||
cfg.Env = env
|
||||
cfg.logger = logger
|
||||
|
|
@ -70,15 +76,28 @@ func (cfg *Config) Init(db *gorm.DB, env *config.AppConfig, logger *logrus.Logge
|
|||
}
|
||||
}
|
||||
|
||||
func (cfg *Config) GetNostrPublicKey() string {
|
||||
func (cfg *config) GetNostrPublicKey() string {
|
||||
return cfg.NostrPublicKey
|
||||
}
|
||||
|
||||
func (cfg *Config) Get(key string, encryptionKey string) (string, error) {
|
||||
func (cfg *config) GetNostrSecretKey() string {
|
||||
return cfg.NostrSecretKey
|
||||
}
|
||||
|
||||
func (cfg *config) GetCookieSecret() string {
|
||||
return cfg.CookieSecret
|
||||
}
|
||||
|
||||
func (cfg *config) GetRelayUrl() string {
|
||||
relayUrl, _ := cfg.Get("Relay", "")
|
||||
return relayUrl
|
||||
}
|
||||
|
||||
func (cfg *config) Get(key string, encryptionKey string) (string, error) {
|
||||
return cfg.get(key, encryptionKey, cfg.db)
|
||||
}
|
||||
|
||||
func (cfg *Config) get(key string, encryptionKey string, db *gorm.DB) (string, error) {
|
||||
func (cfg *config) get(key string, encryptionKey string, db *gorm.DB) (string, error) {
|
||||
var userConfig dbModels.UserConfig
|
||||
err := db.Where(&dbModels.UserConfig{Key: key}).Limit(1).Find(&userConfig).Error
|
||||
if err != nil {
|
||||
|
|
@ -96,7 +115,7 @@ func (cfg *Config) get(key string, encryptionKey string, db *gorm.DB) (string, e
|
|||
return value, nil
|
||||
}
|
||||
|
||||
func (cfg *Config) set(key string, value string, clauses clause.OnConflict, encryptionKey string, db *gorm.DB) error {
|
||||
func (cfg *config) set(key string, value string, clauses clause.OnConflict, encryptionKey string, db *gorm.DB) error {
|
||||
if encryptionKey != "" {
|
||||
encrypted, err := AesGcmEncrypt(value, encryptionKey)
|
||||
if err != nil {
|
||||
|
|
@ -113,7 +132,7 @@ func (cfg *Config) set(key string, value string, clauses clause.OnConflict, encr
|
|||
return nil
|
||||
}
|
||||
|
||||
func (cfg *Config) SetIgnore(key string, value string, encryptionKey string) {
|
||||
func (cfg *config) SetIgnore(key string, value string, encryptionKey string) {
|
||||
clauses := clause.OnConflict{
|
||||
Columns: []clause.Column{{Name: "key"}},
|
||||
DoNothing: true,
|
||||
|
|
@ -124,7 +143,7 @@ func (cfg *Config) SetIgnore(key string, value string, encryptionKey string) {
|
|||
}
|
||||
}
|
||||
|
||||
func (cfg *Config) SetUpdate(key string, value string, encryptionKey string) {
|
||||
func (cfg *config) SetUpdate(key string, value string, encryptionKey string) {
|
||||
clauses := clause.OnConflict{
|
||||
Columns: []clause.Column{{Name: "key"}},
|
||||
DoUpdates: clause.AssignmentColumns([]string{"value"}),
|
||||
|
|
@ -135,7 +154,7 @@ func (cfg *Config) SetUpdate(key string, value string, encryptionKey string) {
|
|||
}
|
||||
}
|
||||
|
||||
func (cfg *Config) ChangeUnlockPassword(currentUnlockPassword string, newUnlockPassword string) error {
|
||||
func (cfg *config) ChangeUnlockPassword(currentUnlockPassword string, newUnlockPassword string) error {
|
||||
if !cfg.CheckUnlockPassword(currentUnlockPassword) {
|
||||
return errors.New("incorrect password")
|
||||
}
|
||||
|
|
@ -179,16 +198,37 @@ func (cfg *Config) ChangeUnlockPassword(currentUnlockPassword string, newUnlockP
|
|||
return nil
|
||||
}
|
||||
|
||||
func (cfg *Config) CheckUnlockPassword(encryptionKey string) bool {
|
||||
func (cfg *config) CheckUnlockPassword(encryptionKey string) bool {
|
||||
decryptedValue, err := cfg.Get("UnlockPasswordCheck", encryptionKey)
|
||||
|
||||
return err == nil && (decryptedValue == "" || decryptedValue == unlockPasswordCheck)
|
||||
}
|
||||
|
||||
func (cfg *Config) SavePasswordCheck(encryptionKey string) {
|
||||
func (cfg *config) Setup(encryptionKey string) {
|
||||
cfg.SetUpdate("UnlockPasswordCheck", unlockPasswordCheck, encryptionKey)
|
||||
}
|
||||
|
||||
func (cfg *config) Start(encryptionKey string) error {
|
||||
nostrSecretKey, _ := cfg.Get("NostrSecretKey", encryptionKey)
|
||||
|
||||
if nostrSecretKey == "" {
|
||||
nostrSecretKey = nostr.GeneratePrivateKey()
|
||||
cfg.SetUpdate("NostrSecretKey", nostrSecretKey, encryptionKey)
|
||||
}
|
||||
nostrPublicKey, err := nostr.GetPublicKey(nostrSecretKey)
|
||||
if err != nil {
|
||||
cfg.logger.WithError(err).Error("Error converting nostr privkey to pubkey")
|
||||
return err
|
||||
}
|
||||
cfg.NostrSecretKey = nostrSecretKey
|
||||
cfg.NostrPublicKey = nostrPublicKey
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cfg *config) GetEnv() *AppConfig {
|
||||
return cfg.Env
|
||||
}
|
||||
|
||||
func randomHex(n int) (string, error) {
|
||||
bytes := make([]byte, n)
|
||||
if _, err := rand.Read(bytes); err != nil {
|
||||
|
|
@ -1,4 +1,3 @@
|
|||
// TODO: move to config/models.go
|
||||
package config
|
||||
|
||||
const (
|
||||
|
|
@ -45,4 +44,12 @@ type Config interface {
|
|||
SetIgnore(key string, value string, encryptionKey string)
|
||||
SetUpdate(key string, value string, encryptionKey string)
|
||||
GetNostrPublicKey() string
|
||||
GetNostrSecretKey() string
|
||||
GetCookieSecret() string
|
||||
GetRelayUrl() string
|
||||
GetEnv() *AppConfig
|
||||
CheckUnlockPassword(password string) bool
|
||||
ChangeUnlockPassword(currentUnlockPassword string, newUnlockPassword string) error
|
||||
Setup(encryptionKey string)
|
||||
Start(encryptionKey string) error
|
||||
}
|
||||
79
db/db_service.go
Normal file
79
db/db_service.go
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
package db
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/getAlby/nostr-wallet-connect/nip47"
|
||||
"github.com/nbd-wtf/go-nostr"
|
||||
"github.com/sirupsen/logrus"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type dbService struct {
|
||||
db *gorm.DB
|
||||
logger *logrus.Logger
|
||||
}
|
||||
|
||||
func NewDBService(db *gorm.DB, logger *logrus.Logger) *dbService {
|
||||
return &dbService{
|
||||
db: db,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
func (dbSvc *dbService) CreateApp(name string, pubkey string, maxAmount int, budgetRenewal string, expiresAt *time.Time, requestMethods []string) (*App, string, error) {
|
||||
var pairingPublicKey string
|
||||
var pairingSecretKey string
|
||||
if pubkey == "" {
|
||||
pairingSecretKey = nostr.GeneratePrivateKey()
|
||||
pairingPublicKey, _ = nostr.GetPublicKey(pairingSecretKey)
|
||||
} else {
|
||||
pairingPublicKey = pubkey
|
||||
//validate public key
|
||||
decoded, err := hex.DecodeString(pairingPublicKey)
|
||||
if err != nil || len(decoded) != 32 {
|
||||
dbSvc.logger.WithField("pairingPublicKey", pairingPublicKey).Error("Invalid public key format")
|
||||
return nil, "", fmt.Errorf("invalid public key format: %s", pairingPublicKey)
|
||||
}
|
||||
}
|
||||
|
||||
app := App{Name: name, NostrPubkey: pairingPublicKey}
|
||||
|
||||
err := dbSvc.db.Transaction(func(tx *gorm.DB) error {
|
||||
err := tx.Save(&app).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, m := range requestMethods {
|
||||
//if we don't know this method, we return an error
|
||||
if !strings.Contains(nip47.CAPABILITIES, m) {
|
||||
return fmt.Errorf("did not recognize request method: %s", m)
|
||||
}
|
||||
appPermission := AppPermission{
|
||||
App: app,
|
||||
RequestMethod: m,
|
||||
ExpiresAt: expiresAt,
|
||||
//these fields are only relevant for pay_invoice
|
||||
MaxAmount: maxAmount,
|
||||
BudgetRenewal: budgetRenewal,
|
||||
}
|
||||
err = tx.Create(&appPermission).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// commit transaction
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
dbSvc.logger.WithError(err).Error("Failed to save app")
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
return &app, pairingSecretKey, nil
|
||||
}
|
||||
83
db/models.go
Normal file
83
db/models.go
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
package db
|
||||
|
||||
import "time"
|
||||
|
||||
type UserConfig struct {
|
||||
ID uint
|
||||
Key string
|
||||
Value string
|
||||
Encrypted bool
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
type App struct {
|
||||
ID uint
|
||||
Name string `validate:"required"`
|
||||
Description string
|
||||
NostrPubkey string `validate:"required"`
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
type AppPermission struct {
|
||||
ID uint
|
||||
AppId uint `validate:"required"`
|
||||
App App
|
||||
RequestMethod string `validate:"required"`
|
||||
MaxAmount int
|
||||
BudgetRenewal string
|
||||
ExpiresAt *time.Time
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
type RequestEvent struct {
|
||||
ID uint
|
||||
AppId *uint
|
||||
App App
|
||||
NostrId string `validate:"required"`
|
||||
Content string
|
||||
State string
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
type ResponseEvent struct {
|
||||
ID uint
|
||||
NostrId string `validate:"required"`
|
||||
RequestId uint `validate:"required"`
|
||||
Content string
|
||||
State string
|
||||
RepliedAt time.Time
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
type Payment struct {
|
||||
ID uint
|
||||
AppId uint `validate:"required"`
|
||||
App App
|
||||
RequestEventId uint `validate:"required"`
|
||||
RequestEvent RequestEvent
|
||||
Amount uint // in sats
|
||||
PaymentRequest string
|
||||
Preimage *string
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
type DBService interface {
|
||||
CreateApp(name string, pubkey string, maxAmount int, budgetRenewal string, expiresAt *time.Time, requestMethods []string) (*App, string, error)
|
||||
}
|
||||
|
||||
const (
|
||||
REQUEST_EVENT_STATE_HANDLER_EXECUTING = "executing"
|
||||
REQUEST_EVENT_STATE_HANDLER_EXECUTED = "executed"
|
||||
REQUEST_EVENT_STATE_HANDLER_ERROR = "error"
|
||||
)
|
||||
const (
|
||||
RESPONSE_EVENT_STATE_PUBLISH_CONFIRMED = "confirmed"
|
||||
RESPONSE_EVENT_STATE_PUBLISH_FAILED = "failed"
|
||||
RESPONSE_EVENT_STATE_PUBLISH_UNCONFIRMED = "unconfirmed"
|
||||
)
|
||||
|
|
@ -8,21 +8,6 @@ import (
|
|||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type EventSubscriber interface {
|
||||
ConsumeEvent(ctx context.Context, event *Event, globalProperties map[string]interface{}) error
|
||||
}
|
||||
|
||||
type Event struct {
|
||||
Event string `json:"event"`
|
||||
Properties interface{} `json:"properties,omitempty"`
|
||||
}
|
||||
|
||||
type PaymentReceivedEventProperties struct {
|
||||
PaymentHash string `json:"payment_hash"`
|
||||
Amount uint64 `json:"amount"`
|
||||
NodeType string `json:"node_type"`
|
||||
}
|
||||
|
||||
type eventPublisher struct {
|
||||
logger *logrus.Logger
|
||||
listeners []EventSubscriber
|
||||
|
|
@ -30,13 +15,6 @@ type eventPublisher struct {
|
|||
globalProperties map[string]interface{}
|
||||
}
|
||||
|
||||
type EventPublisher interface {
|
||||
RegisterSubscriber(eventListener EventSubscriber)
|
||||
RemoveSubscriber(eventListener EventSubscriber)
|
||||
Publish(event *Event)
|
||||
SetGlobalProperty(key string, value interface{})
|
||||
}
|
||||
|
||||
func NewEventPublisher(logger *logrus.Logger) *eventPublisher {
|
||||
eventPublisher := &eventPublisher{
|
||||
logger: logger,
|
||||
|
|
|
|||
25
events/models.go
Normal file
25
events/models.go
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
package events
|
||||
|
||||
import "context"
|
||||
|
||||
type EventSubscriber interface {
|
||||
ConsumeEvent(ctx context.Context, event *Event, globalProperties map[string]interface{}) error
|
||||
}
|
||||
|
||||
type EventPublisher interface {
|
||||
RegisterSubscriber(eventListener EventSubscriber)
|
||||
RemoveSubscriber(eventListener EventSubscriber)
|
||||
Publish(event *Event)
|
||||
SetGlobalProperty(key string, value interface{})
|
||||
}
|
||||
|
||||
type Event struct {
|
||||
Event string `json:"event"`
|
||||
Properties interface{} `json:"properties,omitempty"`
|
||||
}
|
||||
|
||||
type PaymentReceivedEventProperties struct {
|
||||
PaymentHash string `json:"payment_hash"`
|
||||
Amount uint64 `json:"amount"`
|
||||
NodeType string `json:"node_type"`
|
||||
}
|
||||
|
|
@ -14,6 +14,7 @@ import { useAlbyMe } from "src/hooks/useAlbyMe";
|
|||
import { useChannels } from "src/hooks/useChannels";
|
||||
import { useInfo } from "src/hooks/useInfo";
|
||||
import { useNodeConnectionInfo } from "src/hooks/useNodeConnectionInfo";
|
||||
import { backendTypeHasMnemonic } from "src/lib/utils";
|
||||
import useChannelOrderStore from "src/state/ChannelOrderStore";
|
||||
|
||||
function SidebarHint() {
|
||||
|
|
@ -34,7 +35,7 @@ function SidebarHint() {
|
|||
}
|
||||
|
||||
// User has a channel order
|
||||
if (order) {
|
||||
if (order && order.status !== "pay") {
|
||||
return (
|
||||
<SidebarHintCard
|
||||
icon={Zap}
|
||||
|
|
@ -64,7 +65,10 @@ function SidebarHint() {
|
|||
}
|
||||
|
||||
// User has no channels yet
|
||||
if ((info?.backendType === "LDK" || info?.backendType === "GREENLIGHT") && channels?.length === 0) {
|
||||
if (
|
||||
(info?.backendType === "LDK" || info?.backendType === "GREENLIGHT") &&
|
||||
channels?.length === 0
|
||||
) {
|
||||
return (
|
||||
<SidebarHintCard
|
||||
icon={Zap}
|
||||
|
|
@ -77,7 +81,11 @@ function SidebarHint() {
|
|||
}
|
||||
|
||||
// User has not linked their hub to their Alby Account
|
||||
if (albyMe && nodeConnectionInfo && albyMe?.keysend_pubkey !== nodeConnectionInfo?.pubkey) {
|
||||
if (
|
||||
albyMe &&
|
||||
nodeConnectionInfo &&
|
||||
albyMe?.keysend_pubkey !== nodeConnectionInfo?.pubkey
|
||||
) {
|
||||
return (
|
||||
<SidebarHintCard
|
||||
icon={Link2}
|
||||
|
|
@ -89,7 +97,12 @@ function SidebarHint() {
|
|||
);
|
||||
}
|
||||
|
||||
if (info?.backendType === "LDK" && info?.showBackupReminder) {
|
||||
if (
|
||||
info &&
|
||||
backendTypeHasMnemonic(info.backendType) &&
|
||||
(!info.nextBackupReminder ||
|
||||
new Date(info.nextBackupReminder).getTime() < new Date().getTime())
|
||||
) {
|
||||
return (
|
||||
<SidebarHintCard
|
||||
icon={ShieldAlert}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { clsx, type ClassValue } from "clsx";
|
||||
import { BackendType } from "src/types";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
|
|
@ -20,3 +21,7 @@ export function splitSocketAddress(socketAddress: string) {
|
|||
const port = socketAddress.slice(lastColonIndex + 1);
|
||||
return { address, port };
|
||||
}
|
||||
|
||||
export function backendTypeHasMnemonic(backendType: BackendType) {
|
||||
return ["LND", "PHOENIX"].indexOf(backendType) === -1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -119,8 +119,9 @@ export default function Channels() {
|
|||
|
||||
if (
|
||||
!confirm(
|
||||
`Are you sure you want to close the channel with ${nodes.find((node) => node.public_key === nodeId)?.alias ||
|
||||
"Unknown Node"
|
||||
`Are you sure you want to close the channel with ${
|
||||
nodes.find((node) => node.public_key === nodeId)?.alias ||
|
||||
"Unknown Node"
|
||||
}?\n\nNode ID: ${nodeId}\n\nChannel ID: ${channelId}`
|
||||
)
|
||||
) {
|
||||
|
|
@ -139,7 +140,8 @@ export default function Channels() {
|
|||
console.log(`🎬 Closing channel with ${nodeId}`);
|
||||
|
||||
const closeChannelResponse = await request<CloseChannelResponse>(
|
||||
`/api/peers/${nodeId}/channels/${channelId}?force=${closeType === "force close"
|
||||
`/api/peers/${nodeId}/channels/${channelId}?force=${
|
||||
closeType === "force close"
|
||||
}`,
|
||||
{
|
||||
method: "DELETE",
|
||||
|
|
@ -203,36 +205,6 @@ export default function Channels() {
|
|||
}
|
||||
}
|
||||
|
||||
async function stopNode() {
|
||||
try {
|
||||
if (!csrf) {
|
||||
throw new Error("csrf not loaded");
|
||||
}
|
||||
|
||||
if (
|
||||
!confirm(
|
||||
"After restarting, you'll need to re-enter your unlock password."
|
||||
)
|
||||
) {
|
||||
console.error("User cancelled reset");
|
||||
return;
|
||||
}
|
||||
|
||||
await request("/api/stop", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"X-CSRF-Token": csrf,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
await reloadInfo();
|
||||
alert(`🎉 Node stopped`);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
alert("Something went wrong: " + error);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<AppHeader
|
||||
|
|
@ -299,9 +271,6 @@ export default function Channels() {
|
|||
<DropdownMenuItem onClick={resetRouter}>
|
||||
Clear Routing Data
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={stopNode}>
|
||||
Restart
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuGroup>
|
||||
</>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -530,7 +530,8 @@ function PayLightningChannelOrder({ order }: { order: NewChannelOrder }) {
|
|||
channels && prevChannels
|
||||
? channels.find(
|
||||
(newChannel) =>
|
||||
!prevChannels.some((current) => current.id === newChannel.id)
|
||||
!prevChannels.some((current) => current.id === newChannel.id) &&
|
||||
newChannel.fundingTxId
|
||||
)
|
||||
: undefined;
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import { Input } from "src/components/ui/input";
|
|||
import { Label } from "src/components/ui/label";
|
||||
import { useToast } from "src/components/ui/use-toast";
|
||||
import { useInfo } from "src/hooks/useInfo";
|
||||
import { backendTypeHasMnemonic } from "src/lib/utils";
|
||||
|
||||
export function SetupPassword() {
|
||||
const { toast } = useToast();
|
||||
|
|
@ -23,6 +24,9 @@ export function SetupPassword() {
|
|||
|
||||
function onSubmit(e: React.FormEvent) {
|
||||
e.preventDefault();
|
||||
if (!info) {
|
||||
return;
|
||||
}
|
||||
if (store.unlockPassword !== confirmPassword) {
|
||||
toast({
|
||||
title: "Passwords don't match",
|
||||
|
|
@ -31,8 +35,7 @@ export function SetupPassword() {
|
|||
return;
|
||||
}
|
||||
|
||||
// Pre-configured nodes that do not need a mnemonic
|
||||
if (info?.backendType === "LND" || info?.backendType === "PHOENIX") {
|
||||
if (!backendTypeHasMnemonic(info.backendType)) {
|
||||
// NOTE: LND flow does not setup a mnemonic
|
||||
navigate(`/setup/finish`);
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -133,12 +133,11 @@ export interface InfoResponse {
|
|||
backendType: BackendType;
|
||||
setupCompleted: boolean;
|
||||
oauthRedirect: boolean;
|
||||
onboardingCompleted: boolean;
|
||||
albyAccountConnected: boolean;
|
||||
running: boolean;
|
||||
unlocked: boolean;
|
||||
albyAuthUrl: string;
|
||||
showBackupReminder: boolean;
|
||||
nextBackupReminder: string;
|
||||
albyUserIdentifier: string;
|
||||
network?: Network;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package main
|
|||
import (
|
||||
"context"
|
||||
|
||||
"github.com/getAlby/nostr-wallet-connect/db"
|
||||
"github.com/getAlby/nostr-wallet-connect/nip47"
|
||||
"github.com/nbd-wtf/go-nostr"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
|
@ -12,7 +13,7 @@ const (
|
|||
MSAT_PER_SAT = 1000
|
||||
)
|
||||
|
||||
func (svc *Service) HandleGetBalanceEvent(ctx context.Context, nip47Request *Nip47Request, requestEvent *RequestEvent, app *App, publishResponse func(*Nip47Response, nostr.Tags)) {
|
||||
func (svc *Service) HandleGetBalanceEvent(ctx context.Context, nip47Request *nip47.Request, requestEvent *db.RequestEvent, app *db.App, publishResponse func(*nip47.Response, nostr.Tags)) {
|
||||
|
||||
resp := svc.checkPermission(nip47Request, requestEvent.NostrId, app, 0)
|
||||
if resp != nil {
|
||||
|
|
@ -20,20 +21,20 @@ func (svc *Service) HandleGetBalanceEvent(ctx context.Context, nip47Request *Nip
|
|||
return
|
||||
}
|
||||
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
"requestEventNostrId": requestEvent.NostrId,
|
||||
"appId": app.ID,
|
||||
}).Info("Fetching balance")
|
||||
|
||||
balance, err := svc.lnClient.GetBalance(ctx)
|
||||
if err != nil {
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
"requestEventNostrId": requestEvent.NostrId,
|
||||
"appId": app.ID,
|
||||
}).Infof("Failed to fetch balance: %v", err)
|
||||
publishResponse(&Nip47Response{
|
||||
publishResponse(&nip47.Response{
|
||||
ResultType: nip47Request.Method,
|
||||
Error: &Nip47Error{
|
||||
Error: &nip47.Error{
|
||||
Code: nip47.ERROR_INTERNAL,
|
||||
Message: err.Error(),
|
||||
},
|
||||
|
|
@ -41,11 +42,11 @@ func (svc *Service) HandleGetBalanceEvent(ctx context.Context, nip47Request *Nip
|
|||
return
|
||||
}
|
||||
|
||||
responsePayload := &Nip47BalanceResponse{
|
||||
responsePayload := &nip47.BalanceResponse{
|
||||
Balance: balance,
|
||||
}
|
||||
|
||||
appPermission := AppPermission{}
|
||||
appPermission := db.AppPermission{}
|
||||
svc.db.Where("app_id = ? AND request_method = ?", app.ID, nip47.PAY_INVOICE_METHOD).First(&appPermission)
|
||||
|
||||
maxAmount := appPermission.MaxAmount
|
||||
|
|
@ -54,7 +55,7 @@ func (svc *Service) HandleGetBalanceEvent(ctx context.Context, nip47Request *Nip
|
|||
responsePayload.BudgetRenewal = appPermission.BudgetRenewal
|
||||
}
|
||||
|
||||
publishResponse(&Nip47Response{
|
||||
publishResponse(&nip47.Response{
|
||||
ResultType: nip47Request.Method,
|
||||
Result: responsePayload,
|
||||
}, nostr.Tags{})
|
||||
|
|
|
|||
|
|
@ -3,12 +3,13 @@ package main
|
|||
import (
|
||||
"context"
|
||||
|
||||
"github.com/getAlby/nostr-wallet-connect/db"
|
||||
"github.com/getAlby/nostr-wallet-connect/nip47"
|
||||
"github.com/nbd-wtf/go-nostr"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func (svc *Service) HandleGetInfoEvent(ctx context.Context, nip47Request *Nip47Request, requestEvent *RequestEvent, app *App, publishResponse func(*Nip47Response, nostr.Tags)) {
|
||||
func (svc *Service) HandleGetInfoEvent(ctx context.Context, nip47Request *nip47.Request, requestEvent *db.RequestEvent, app *db.App, publishResponse func(*nip47.Response, nostr.Tags)) {
|
||||
|
||||
resp := svc.checkPermission(nip47Request, requestEvent.NostrId, app, 0)
|
||||
if resp != nil {
|
||||
|
|
@ -16,21 +17,21 @@ func (svc *Service) HandleGetInfoEvent(ctx context.Context, nip47Request *Nip47R
|
|||
return
|
||||
}
|
||||
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
"requestEventNostrId": requestEvent.NostrId,
|
||||
"appId": app.ID,
|
||||
}).Info("Fetching node info")
|
||||
|
||||
info, err := svc.lnClient.GetInfo(ctx)
|
||||
if err != nil {
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
"requestEventNostrId": requestEvent.NostrId,
|
||||
"appId": app.ID,
|
||||
}).Infof("Failed to fetch node info: %v", err)
|
||||
|
||||
publishResponse(&Nip47Response{
|
||||
publishResponse(&nip47.Response{
|
||||
ResultType: nip47Request.Method,
|
||||
Error: &Nip47Error{
|
||||
Error: &nip47.Error{
|
||||
Code: nip47.ERROR_INTERNAL,
|
||||
Message: err.Error(),
|
||||
},
|
||||
|
|
@ -44,7 +45,7 @@ func (svc *Service) HandleGetInfoEvent(ctx context.Context, nip47Request *Nip47R
|
|||
network = "mainnet"
|
||||
}
|
||||
|
||||
responsePayload := &Nip47GetInfoResponse{
|
||||
responsePayload := &nip47.GetInfoResponse{
|
||||
Alias: info.Alias,
|
||||
Color: info.Color,
|
||||
Pubkey: info.Pubkey,
|
||||
|
|
@ -54,7 +55,7 @@ func (svc *Service) HandleGetInfoEvent(ctx context.Context, nip47Request *Nip47R
|
|||
Methods: svc.GetMethods(app),
|
||||
}
|
||||
|
||||
publishResponse(&Nip47Response{
|
||||
publishResponse(&nip47.Response{
|
||||
ResultType: nip47Request.Method,
|
||||
Result: responsePayload,
|
||||
}, nostr.Tags{})
|
||||
|
|
|
|||
|
|
@ -3,14 +3,15 @@ package main
|
|||
import (
|
||||
"context"
|
||||
|
||||
"github.com/getAlby/nostr-wallet-connect/db"
|
||||
"github.com/getAlby/nostr-wallet-connect/nip47"
|
||||
"github.com/nbd-wtf/go-nostr"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func (svc *Service) HandleListTransactionsEvent(ctx context.Context, nip47Request *Nip47Request, requestEvent *RequestEvent, app *App, publishResponse func(*Nip47Response, nostr.Tags)) {
|
||||
func (svc *Service) HandleListTransactionsEvent(ctx context.Context, nip47Request *nip47.Request, requestEvent *db.RequestEvent, app *db.App, publishResponse func(*nip47.Response, nostr.Tags)) {
|
||||
|
||||
listParams := &Nip47ListTransactionsParams{}
|
||||
listParams := &nip47.ListTransactionsParams{}
|
||||
resp := svc.decodeNip47Request(nip47Request, requestEvent, app, listParams)
|
||||
if resp != nil {
|
||||
publishResponse(resp, nostr.Tags{})
|
||||
|
|
@ -23,7 +24,7 @@ func (svc *Service) HandleListTransactionsEvent(ctx context.Context, nip47Reques
|
|||
return
|
||||
}
|
||||
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
// TODO: log request fields from listParams
|
||||
"requestEventNostrId": requestEvent.NostrId,
|
||||
"appId": app.ID,
|
||||
|
|
@ -37,15 +38,15 @@ func (svc *Service) HandleListTransactionsEvent(ctx context.Context, nip47Reques
|
|||
}
|
||||
transactions, err := svc.lnClient.ListTransactions(ctx, listParams.From, listParams.Until, limit, listParams.Offset, listParams.Unpaid, listParams.Type)
|
||||
if err != nil {
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
// TODO: log request fields from listParams
|
||||
"requestEventNostrId": requestEvent.NostrId,
|
||||
"appId": app.ID,
|
||||
}).Infof("Failed to fetch transactions: %v", err)
|
||||
|
||||
publishResponse(&Nip47Response{
|
||||
publishResponse(&nip47.Response{
|
||||
ResultType: nip47Request.Method,
|
||||
Error: &Nip47Error{
|
||||
Error: &nip47.Error{
|
||||
Code: nip47.ERROR_INTERNAL,
|
||||
Message: err.Error(),
|
||||
},
|
||||
|
|
@ -53,11 +54,11 @@ func (svc *Service) HandleListTransactionsEvent(ctx context.Context, nip47Reques
|
|||
return
|
||||
}
|
||||
|
||||
responsePayload := &Nip47ListTransactionsResponse{
|
||||
responsePayload := &nip47.ListTransactionsResponse{
|
||||
Transactions: transactions,
|
||||
}
|
||||
|
||||
publishResponse(&Nip47Response{
|
||||
publishResponse(&nip47.Response{
|
||||
ResultType: nip47Request.Method,
|
||||
Result: responsePayload,
|
||||
}, nostr.Tags{})
|
||||
|
|
|
|||
|
|
@ -5,15 +5,16 @@ import (
|
|||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/getAlby/nostr-wallet-connect/db"
|
||||
"github.com/getAlby/nostr-wallet-connect/nip47"
|
||||
"github.com/nbd-wtf/go-nostr"
|
||||
decodepay "github.com/nbd-wtf/ln-decodepay"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func (svc *Service) HandleLookupInvoiceEvent(ctx context.Context, nip47Request *Nip47Request, requestEvent *RequestEvent, app *App, publishResponse func(*Nip47Response, nostr.Tags)) {
|
||||
func (svc *Service) HandleLookupInvoiceEvent(ctx context.Context, nip47Request *nip47.Request, requestEvent *db.RequestEvent, app *db.App, publishResponse func(*nip47.Response, nostr.Tags)) {
|
||||
|
||||
lookupInvoiceParams := &Nip47LookupInvoiceParams{}
|
||||
lookupInvoiceParams := &nip47.LookupInvoiceParams{}
|
||||
resp := svc.decodeNip47Request(nip47Request, requestEvent, app, lookupInvoiceParams)
|
||||
if resp != nil {
|
||||
publishResponse(resp, nostr.Tags{})
|
||||
|
|
@ -26,7 +27,7 @@ func (svc *Service) HandleLookupInvoiceEvent(ctx context.Context, nip47Request *
|
|||
return
|
||||
}
|
||||
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
"requestEventNostrId": requestEvent.NostrId,
|
||||
"appId": app.ID,
|
||||
"invoice": lookupInvoiceParams.Invoice,
|
||||
|
|
@ -38,15 +39,15 @@ func (svc *Service) HandleLookupInvoiceEvent(ctx context.Context, nip47Request *
|
|||
if paymentHash == "" {
|
||||
paymentRequest, err := decodepay.Decodepay(strings.ToLower(lookupInvoiceParams.Invoice))
|
||||
if err != nil {
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
"requestEventNostrId": requestEvent.NostrId,
|
||||
"appId": app.ID,
|
||||
"invoice": lookupInvoiceParams.Invoice,
|
||||
}).Errorf("Failed to decode bolt11 invoice: %v", err)
|
||||
|
||||
publishResponse(&Nip47Response{
|
||||
publishResponse(&nip47.Response{
|
||||
ResultType: nip47Request.Method,
|
||||
Error: &Nip47Error{
|
||||
Error: &nip47.Error{
|
||||
Code: nip47.ERROR_INTERNAL,
|
||||
Message: fmt.Sprintf("Failed to decode bolt11 invoice: %s", err.Error()),
|
||||
},
|
||||
|
|
@ -58,16 +59,16 @@ func (svc *Service) HandleLookupInvoiceEvent(ctx context.Context, nip47Request *
|
|||
|
||||
transaction, err := svc.lnClient.LookupInvoice(ctx, paymentHash)
|
||||
if err != nil {
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
"requestEventNostrId": requestEvent.NostrId,
|
||||
"appId": app.ID,
|
||||
"invoice": lookupInvoiceParams.Invoice,
|
||||
"paymentHash": lookupInvoiceParams.PaymentHash,
|
||||
}).Infof("Failed to lookup invoice: %v", err)
|
||||
|
||||
publishResponse(&Nip47Response{
|
||||
publishResponse(&nip47.Response{
|
||||
ResultType: nip47Request.Method,
|
||||
Error: &Nip47Error{
|
||||
Error: &nip47.Error{
|
||||
Code: nip47.ERROR_INTERNAL,
|
||||
Message: err.Error(),
|
||||
},
|
||||
|
|
@ -75,11 +76,11 @@ func (svc *Service) HandleLookupInvoiceEvent(ctx context.Context, nip47Request *
|
|||
return
|
||||
}
|
||||
|
||||
responsePayload := &Nip47LookupInvoiceResponse{
|
||||
Nip47Transaction: *transaction,
|
||||
responsePayload := &nip47.LookupInvoiceResponse{
|
||||
Transaction: *transaction,
|
||||
}
|
||||
|
||||
publishResponse(&Nip47Response{
|
||||
publishResponse(&nip47.Response{
|
||||
ResultType: nip47Request.Method,
|
||||
Result: responsePayload,
|
||||
}, nostr.Tags{})
|
||||
|
|
|
|||
|
|
@ -3,14 +3,15 @@ package main
|
|||
import (
|
||||
"context"
|
||||
|
||||
"github.com/getAlby/nostr-wallet-connect/db"
|
||||
"github.com/getAlby/nostr-wallet-connect/nip47"
|
||||
"github.com/nbd-wtf/go-nostr"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func (svc *Service) HandleMakeInvoiceEvent(ctx context.Context, nip47Request *Nip47Request, requestEvent *RequestEvent, app *App, publishResponse func(*Nip47Response, nostr.Tags)) {
|
||||
func (svc *Service) HandleMakeInvoiceEvent(ctx context.Context, nip47Request *nip47.Request, requestEvent *db.RequestEvent, app *db.App, publishResponse func(*nip47.Response, nostr.Tags)) {
|
||||
|
||||
makeInvoiceParams := &Nip47MakeInvoiceParams{}
|
||||
makeInvoiceParams := &nip47.MakeInvoiceParams{}
|
||||
resp := svc.decodeNip47Request(nip47Request, requestEvent, app, makeInvoiceParams)
|
||||
if resp != nil {
|
||||
publishResponse(resp, nostr.Tags{})
|
||||
|
|
@ -23,7 +24,7 @@ func (svc *Service) HandleMakeInvoiceEvent(ctx context.Context, nip47Request *Ni
|
|||
return
|
||||
}
|
||||
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
"requestEventNostrId": requestEvent.NostrId,
|
||||
"appId": app.ID,
|
||||
"amount": makeInvoiceParams.Amount,
|
||||
|
|
@ -39,7 +40,7 @@ func (svc *Service) HandleMakeInvoiceEvent(ctx context.Context, nip47Request *Ni
|
|||
|
||||
transaction, err := svc.lnClient.MakeInvoice(ctx, makeInvoiceParams.Amount, makeInvoiceParams.Description, makeInvoiceParams.DescriptionHash, expiry)
|
||||
if err != nil {
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
"requestEventNostrId": requestEvent.NostrId,
|
||||
"appId": app.ID,
|
||||
"amount": makeInvoiceParams.Amount,
|
||||
|
|
@ -48,9 +49,9 @@ func (svc *Service) HandleMakeInvoiceEvent(ctx context.Context, nip47Request *Ni
|
|||
"expiry": makeInvoiceParams.Expiry,
|
||||
}).Infof("Failed to make invoice: %v", err)
|
||||
|
||||
publishResponse(&Nip47Response{
|
||||
publishResponse(&nip47.Response{
|
||||
ResultType: nip47Request.Method,
|
||||
Error: &Nip47Error{
|
||||
Error: &nip47.Error{
|
||||
Code: nip47.ERROR_INTERNAL,
|
||||
Message: err.Error(),
|
||||
},
|
||||
|
|
@ -58,11 +59,11 @@ func (svc *Service) HandleMakeInvoiceEvent(ctx context.Context, nip47Request *Ni
|
|||
return
|
||||
}
|
||||
|
||||
responsePayload := &Nip47MakeInvoiceResponse{
|
||||
Nip47Transaction: *transaction,
|
||||
responsePayload := &nip47.MakeInvoiceResponse{
|
||||
Transaction: *transaction,
|
||||
}
|
||||
|
||||
publishResponse(&Nip47Response{
|
||||
publishResponse(&nip47.Response{
|
||||
ResultType: nip47Request.Method,
|
||||
Result: responsePayload,
|
||||
}, nostr.Tags{})
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import (
|
|||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/getAlby/nostr-wallet-connect/db"
|
||||
"github.com/getAlby/nostr-wallet-connect/events"
|
||||
"github.com/getAlby/nostr-wallet-connect/nip47"
|
||||
"github.com/nbd-wtf/go-nostr"
|
||||
|
|
@ -14,9 +15,9 @@ import (
|
|||
)
|
||||
|
||||
// TODO: pass a channel instead of publishResponse function
|
||||
func (svc *Service) HandleMultiPayInvoiceEvent(ctx context.Context, nip47Request *Nip47Request, requestEvent *RequestEvent, app *App, publishResponse func(*Nip47Response, nostr.Tags)) {
|
||||
func (svc *Service) HandleMultiPayInvoiceEvent(ctx context.Context, nip47Request *nip47.Request, requestEvent *db.RequestEvent, app *db.App, publishResponse func(*nip47.Response, nostr.Tags)) {
|
||||
|
||||
multiPayParams := &Nip47MultiPayInvoiceParams{}
|
||||
multiPayParams := &nip47.MultiPayInvoiceParams{}
|
||||
resp := svc.decodeNip47Request(nip47Request, requestEvent, app, multiPayParams)
|
||||
if resp != nil {
|
||||
publishResponse(resp, nostr.Tags{})
|
||||
|
|
@ -28,14 +29,14 @@ func (svc *Service) HandleMultiPayInvoiceEvent(ctx context.Context, nip47Request
|
|||
for _, invoiceInfo := range multiPayParams.Invoices {
|
||||
wg.Add(1)
|
||||
// TODO: we should call the handle_payment_request (most of this code is duplicated)
|
||||
go func(invoiceInfo Nip47MultiPayInvoiceElement) {
|
||||
go func(invoiceInfo nip47.MultiPayInvoiceElement) {
|
||||
defer wg.Done()
|
||||
bolt11 := invoiceInfo.Invoice
|
||||
// Convert invoice to lowercase string
|
||||
bolt11 = strings.ToLower(bolt11)
|
||||
paymentRequest, err := decodepay.Decodepay(bolt11)
|
||||
if err != nil {
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
"requestEventNostrId": requestEvent.NostrId,
|
||||
"appId": app.ID,
|
||||
"bolt11": bolt11,
|
||||
|
|
@ -43,9 +44,9 @@ func (svc *Service) HandleMultiPayInvoiceEvent(ctx context.Context, nip47Request
|
|||
|
||||
// TODO: Decide what to do if id is empty
|
||||
dTag := []string{"d", invoiceInfo.Id}
|
||||
publishResponse(&Nip47Response{
|
||||
publishResponse(&nip47.Response{
|
||||
ResultType: nip47Request.Method,
|
||||
Error: &Nip47Error{
|
||||
Error: &nip47.Error{
|
||||
Code: nip47.ERROR_INTERNAL,
|
||||
Message: fmt.Sprintf("Failed to decode bolt11 invoice: %s", err.Error()),
|
||||
},
|
||||
|
|
@ -65,12 +66,12 @@ func (svc *Service) HandleMultiPayInvoiceEvent(ctx context.Context, nip47Request
|
|||
return
|
||||
}
|
||||
|
||||
payment := Payment{App: *app, RequestEventId: requestEvent.ID, PaymentRequest: bolt11, Amount: uint(paymentRequest.MSatoshi / 1000)}
|
||||
payment := db.Payment{App: *app, RequestEventId: requestEvent.ID, PaymentRequest: bolt11, Amount: uint(paymentRequest.MSatoshi / 1000)}
|
||||
mu.Lock()
|
||||
insertPaymentResult := svc.db.Create(&payment)
|
||||
mu.Unlock()
|
||||
if insertPaymentResult.Error != nil {
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
"requestEventNostrId": requestEvent.NostrId,
|
||||
"paymentRequest": bolt11,
|
||||
"invoiceId": invoiceInfo.Id,
|
||||
|
|
@ -78,7 +79,7 @@ func (svc *Service) HandleMultiPayInvoiceEvent(ctx context.Context, nip47Request
|
|||
return
|
||||
}
|
||||
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
"requestEventNostrId": requestEvent.NostrId,
|
||||
"appId": app.ID,
|
||||
"bolt11": bolt11,
|
||||
|
|
@ -86,13 +87,13 @@ func (svc *Service) HandleMultiPayInvoiceEvent(ctx context.Context, nip47Request
|
|||
|
||||
response, err := svc.lnClient.SendPaymentSync(ctx, bolt11)
|
||||
if err != nil {
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
"requestEventNostrId": requestEvent.NostrId,
|
||||
"appId": app.ID,
|
||||
"bolt11": bolt11,
|
||||
}).Infof("Failed to send payment: %v", err)
|
||||
|
||||
svc.EventPublisher.Publish(&events.Event{
|
||||
svc.eventPublisher.Publish(&events.Event{
|
||||
Event: "nwc_payment_failed",
|
||||
Properties: map[string]interface{}{
|
||||
// "error": fmt.Sprintf("%v", err),
|
||||
|
|
@ -102,9 +103,9 @@ func (svc *Service) HandleMultiPayInvoiceEvent(ctx context.Context, nip47Request
|
|||
},
|
||||
})
|
||||
|
||||
publishResponse(&Nip47Response{
|
||||
publishResponse(&nip47.Response{
|
||||
ResultType: nip47Request.Method,
|
||||
Error: &Nip47Error{
|
||||
Error: &nip47.Error{
|
||||
Code: nip47.ERROR_INTERNAL,
|
||||
Message: err.Error(),
|
||||
},
|
||||
|
|
@ -117,16 +118,16 @@ func (svc *Service) HandleMultiPayInvoiceEvent(ctx context.Context, nip47Request
|
|||
mu.Lock()
|
||||
svc.db.Save(&payment)
|
||||
mu.Unlock()
|
||||
svc.EventPublisher.Publish(&events.Event{
|
||||
svc.eventPublisher.Publish(&events.Event{
|
||||
Event: "nwc_payment_succeeded",
|
||||
Properties: map[string]interface{}{
|
||||
"multi": true,
|
||||
"amount": paymentRequest.MSatoshi / 1000,
|
||||
},
|
||||
})
|
||||
publishResponse(&Nip47Response{
|
||||
publishResponse(&nip47.Response{
|
||||
ResultType: nip47Request.Method,
|
||||
Result: Nip47PayResponse{
|
||||
Result: nip47.PayResponse{
|
||||
Preimage: response.Preimage,
|
||||
FeesPaid: response.Fee,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -4,15 +4,16 @@ import (
|
|||
"context"
|
||||
"sync"
|
||||
|
||||
"github.com/getAlby/nostr-wallet-connect/db"
|
||||
"github.com/getAlby/nostr-wallet-connect/events"
|
||||
"github.com/getAlby/nostr-wallet-connect/nip47"
|
||||
"github.com/nbd-wtf/go-nostr"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func (svc *Service) HandleMultiPayKeysendEvent(ctx context.Context, nip47Request *Nip47Request, requestEvent *RequestEvent, app *App, publishResponse func(*Nip47Response, nostr.Tags)) {
|
||||
func (svc *Service) HandleMultiPayKeysendEvent(ctx context.Context, nip47Request *nip47.Request, requestEvent *db.RequestEvent, app *db.App, publishResponse func(*nip47.Response, nostr.Tags)) {
|
||||
|
||||
multiPayParams := &Nip47MultiPayKeysendParams{}
|
||||
multiPayParams := &nip47.MultiPayKeysendParams{}
|
||||
resp := svc.decodeNip47Request(nip47Request, requestEvent, app, multiPayParams)
|
||||
if resp != nil {
|
||||
publishResponse(resp, nostr.Tags{})
|
||||
|
|
@ -23,7 +24,7 @@ func (svc *Service) HandleMultiPayKeysendEvent(ctx context.Context, nip47Request
|
|||
var mu sync.Mutex
|
||||
for _, keysendInfo := range multiPayParams.Keysends {
|
||||
wg.Add(1)
|
||||
go func(keysendInfo Nip47MultiPayKeysendElement) {
|
||||
go func(keysendInfo nip47.MultiPayKeysendElement) {
|
||||
defer wg.Done()
|
||||
|
||||
keysendDTagValue := keysendInfo.Id
|
||||
|
|
@ -38,12 +39,12 @@ func (svc *Service) HandleMultiPayKeysendEvent(ctx context.Context, nip47Request
|
|||
return
|
||||
}
|
||||
|
||||
payment := Payment{App: *app, RequestEvent: *requestEvent, Amount: uint(keysendInfo.Amount / 1000)}
|
||||
payment := db.Payment{App: *app, RequestEvent: *requestEvent, Amount: uint(keysendInfo.Amount / 1000)}
|
||||
mu.Lock()
|
||||
insertPaymentResult := svc.db.Create(&payment)
|
||||
mu.Unlock()
|
||||
if insertPaymentResult.Error != nil {
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
"requestEventNostrId": requestEvent.NostrId,
|
||||
"recipientPubkey": keysendInfo.Pubkey,
|
||||
"keysendId": keysendInfo.Id,
|
||||
|
|
@ -51,7 +52,7 @@ func (svc *Service) HandleMultiPayKeysendEvent(ctx context.Context, nip47Request
|
|||
return
|
||||
}
|
||||
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
"requestEventNostrId": requestEvent.NostrId,
|
||||
"appId": app.ID,
|
||||
"recipientPubkey": keysendInfo.Pubkey,
|
||||
|
|
@ -59,12 +60,12 @@ func (svc *Service) HandleMultiPayKeysendEvent(ctx context.Context, nip47Request
|
|||
|
||||
preimage, err := svc.lnClient.SendKeysend(ctx, keysendInfo.Amount, keysendInfo.Pubkey, keysendInfo.Preimage, keysendInfo.TLVRecords)
|
||||
if err != nil {
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
"requestEventNostrId": requestEvent.NostrId,
|
||||
"appId": app.ID,
|
||||
"recipientPubkey": keysendInfo.Pubkey,
|
||||
}).Infof("Failed to send payment: %v", err)
|
||||
svc.EventPublisher.Publish(&events.Event{
|
||||
svc.eventPublisher.Publish(&events.Event{
|
||||
Event: "nwc_payment_failed",
|
||||
Properties: map[string]interface{}{
|
||||
// "error": fmt.Sprintf("%v", err),
|
||||
|
|
@ -74,9 +75,9 @@ func (svc *Service) HandleMultiPayKeysendEvent(ctx context.Context, nip47Request
|
|||
},
|
||||
})
|
||||
|
||||
publishResponse(&Nip47Response{
|
||||
publishResponse(&nip47.Response{
|
||||
ResultType: nip47Request.Method,
|
||||
Error: &Nip47Error{
|
||||
Error: &nip47.Error{
|
||||
Code: nip47.ERROR_INTERNAL,
|
||||
Message: err.Error(),
|
||||
},
|
||||
|
|
@ -87,7 +88,7 @@ func (svc *Service) HandleMultiPayKeysendEvent(ctx context.Context, nip47Request
|
|||
mu.Lock()
|
||||
svc.db.Save(&payment)
|
||||
mu.Unlock()
|
||||
svc.EventPublisher.Publish(&events.Event{
|
||||
svc.eventPublisher.Publish(&events.Event{
|
||||
Event: "nwc_payment_succeeded",
|
||||
Properties: map[string]interface{}{
|
||||
"keysend": true,
|
||||
|
|
@ -95,9 +96,9 @@ func (svc *Service) HandleMultiPayKeysendEvent(ctx context.Context, nip47Request
|
|||
"amount": keysendInfo.Amount / 1000,
|
||||
},
|
||||
})
|
||||
publishResponse(&Nip47Response{
|
||||
publishResponse(&nip47.Response{
|
||||
ResultType: nip47Request.Method,
|
||||
Result: Nip47PayResponse{
|
||||
Result: nip47.PayResponse{
|
||||
Preimage: preimage,
|
||||
},
|
||||
}, nostr.Tags{dTag})
|
||||
|
|
|
|||
|
|
@ -3,15 +3,16 @@ package main
|
|||
import (
|
||||
"context"
|
||||
|
||||
"github.com/getAlby/nostr-wallet-connect/db"
|
||||
"github.com/getAlby/nostr-wallet-connect/events"
|
||||
"github.com/getAlby/nostr-wallet-connect/nip47"
|
||||
"github.com/nbd-wtf/go-nostr"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func (svc *Service) HandlePayKeysendEvent(ctx context.Context, nip47Request *Nip47Request, requestEvent *RequestEvent, app *App, publishResponse func(*Nip47Response, nostr.Tags)) {
|
||||
func (svc *Service) HandlePayKeysendEvent(ctx context.Context, nip47Request *nip47.Request, requestEvent *db.RequestEvent, app *db.App, publishResponse func(*nip47.Response, nostr.Tags)) {
|
||||
|
||||
payParams := &Nip47KeysendParams{}
|
||||
payParams := &nip47.KeysendParams{}
|
||||
resp := svc.decodeNip47Request(nip47Request, requestEvent, app, payParams)
|
||||
if resp != nil {
|
||||
publishResponse(resp, nostr.Tags{})
|
||||
|
|
@ -24,12 +25,12 @@ func (svc *Service) HandlePayKeysendEvent(ctx context.Context, nip47Request *Nip
|
|||
return
|
||||
}
|
||||
|
||||
payment := Payment{App: *app, RequestEvent: *requestEvent, Amount: uint(payParams.Amount / 1000)}
|
||||
payment := db.Payment{App: *app, RequestEvent: *requestEvent, Amount: uint(payParams.Amount / 1000)}
|
||||
err := svc.db.Create(&payment).Error
|
||||
if err != nil {
|
||||
publishResponse(&Nip47Response{
|
||||
publishResponse(&nip47.Response{
|
||||
ResultType: nip47Request.Method,
|
||||
Error: &Nip47Error{
|
||||
Error: &nip47.Error{
|
||||
Code: nip47.ERROR_INTERNAL,
|
||||
Message: err.Error(),
|
||||
},
|
||||
|
|
@ -37,7 +38,7 @@ func (svc *Service) HandlePayKeysendEvent(ctx context.Context, nip47Request *Nip
|
|||
return
|
||||
}
|
||||
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
"requestEventNostrId": requestEvent.NostrId,
|
||||
"appId": app.ID,
|
||||
"senderPubkey": payParams.Pubkey,
|
||||
|
|
@ -45,12 +46,12 @@ func (svc *Service) HandlePayKeysendEvent(ctx context.Context, nip47Request *Nip
|
|||
|
||||
preimage, err := svc.lnClient.SendKeysend(ctx, payParams.Amount, payParams.Pubkey, payParams.Preimage, payParams.TLVRecords)
|
||||
if err != nil {
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
"requestEventNostrId": requestEvent.NostrId,
|
||||
"appId": app.ID,
|
||||
"recipientPubkey": payParams.Pubkey,
|
||||
}).Infof("Failed to send payment: %v", err)
|
||||
svc.EventPublisher.Publish(&events.Event{
|
||||
svc.eventPublisher.Publish(&events.Event{
|
||||
Event: "nwc_payment_failed",
|
||||
Properties: map[string]interface{}{
|
||||
// "error": fmt.Sprintf("%v", err),
|
||||
|
|
@ -58,9 +59,9 @@ func (svc *Service) HandlePayKeysendEvent(ctx context.Context, nip47Request *Nip
|
|||
"amount": payParams.Amount / 1000,
|
||||
},
|
||||
})
|
||||
publishResponse(&Nip47Response{
|
||||
publishResponse(&nip47.Response{
|
||||
ResultType: nip47Request.Method,
|
||||
Error: &Nip47Error{
|
||||
Error: &nip47.Error{
|
||||
Code: nip47.ERROR_INTERNAL,
|
||||
Message: err.Error(),
|
||||
},
|
||||
|
|
@ -69,16 +70,16 @@ func (svc *Service) HandlePayKeysendEvent(ctx context.Context, nip47Request *Nip
|
|||
}
|
||||
payment.Preimage = &preimage
|
||||
svc.db.Save(&payment)
|
||||
svc.EventPublisher.Publish(&events.Event{
|
||||
svc.eventPublisher.Publish(&events.Event{
|
||||
Event: "nwc_payment_succeeded",
|
||||
Properties: map[string]interface{}{
|
||||
"keysend": true,
|
||||
"amount": payParams.Amount / 1000,
|
||||
},
|
||||
})
|
||||
publishResponse(&Nip47Response{
|
||||
publishResponse(&nip47.Response{
|
||||
ResultType: nip47Request.Method,
|
||||
Result: Nip47PayResponse{
|
||||
Result: nip47.PayResponse{
|
||||
Preimage: preimage,
|
||||
},
|
||||
}, nostr.Tags{})
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import (
|
|||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/getAlby/nostr-wallet-connect/db"
|
||||
"github.com/getAlby/nostr-wallet-connect/events"
|
||||
"github.com/getAlby/nostr-wallet-connect/nip47"
|
||||
"github.com/nbd-wtf/go-nostr"
|
||||
|
|
@ -12,9 +13,9 @@ import (
|
|||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func (svc *Service) HandlePayInvoiceEvent(ctx context.Context, nip47Request *Nip47Request, requestEvent *RequestEvent, app *App, publishResponse func(*Nip47Response, nostr.Tags)) {
|
||||
func (svc *Service) HandlePayInvoiceEvent(ctx context.Context, nip47Request *nip47.Request, requestEvent *db.RequestEvent, app *db.App, publishResponse func(*nip47.Response, nostr.Tags)) {
|
||||
|
||||
payParams := &Nip47PayParams{}
|
||||
payParams := &nip47.PayParams{}
|
||||
resp := svc.decodeNip47Request(nip47Request, requestEvent, app, payParams)
|
||||
if resp != nil {
|
||||
publishResponse(resp, nostr.Tags{})
|
||||
|
|
@ -26,15 +27,15 @@ func (svc *Service) HandlePayInvoiceEvent(ctx context.Context, nip47Request *Nip
|
|||
bolt11 = strings.ToLower(bolt11)
|
||||
paymentRequest, err := decodepay.Decodepay(bolt11)
|
||||
if err != nil {
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
"requestEventNostrId": requestEvent.NostrId,
|
||||
"appId": app.ID,
|
||||
"bolt11": bolt11,
|
||||
}).Errorf("Failed to decode bolt11 invoice: %v", err)
|
||||
|
||||
publishResponse(&Nip47Response{
|
||||
publishResponse(&nip47.Response{
|
||||
ResultType: nip47Request.Method,
|
||||
Error: &Nip47Error{
|
||||
Error: &nip47.Error{
|
||||
Code: nip47.ERROR_INTERNAL,
|
||||
Message: fmt.Sprintf("Failed to decode bolt11 invoice: %s", err.Error()),
|
||||
},
|
||||
|
|
@ -48,12 +49,12 @@ func (svc *Service) HandlePayInvoiceEvent(ctx context.Context, nip47Request *Nip
|
|||
return
|
||||
}
|
||||
|
||||
payment := Payment{App: *app, RequestEvent: *requestEvent, PaymentRequest: bolt11, Amount: uint(paymentRequest.MSatoshi / 1000)}
|
||||
payment := db.Payment{App: *app, RequestEvent: *requestEvent, PaymentRequest: bolt11, Amount: uint(paymentRequest.MSatoshi / 1000)}
|
||||
err = svc.db.Create(&payment).Error
|
||||
if err != nil {
|
||||
publishResponse(&Nip47Response{
|
||||
publishResponse(&nip47.Response{
|
||||
ResultType: nip47Request.Method,
|
||||
Error: &Nip47Error{
|
||||
Error: &nip47.Error{
|
||||
Code: nip47.ERROR_INTERNAL,
|
||||
Message: err.Error(),
|
||||
},
|
||||
|
|
@ -61,7 +62,7 @@ func (svc *Service) HandlePayInvoiceEvent(ctx context.Context, nip47Request *Nip
|
|||
return
|
||||
}
|
||||
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
"requestEventNostrId": requestEvent.NostrId,
|
||||
"appId": app.ID,
|
||||
"bolt11": bolt11,
|
||||
|
|
@ -69,12 +70,12 @@ func (svc *Service) HandlePayInvoiceEvent(ctx context.Context, nip47Request *Nip
|
|||
|
||||
response, err := svc.lnClient.SendPaymentSync(ctx, bolt11)
|
||||
if err != nil {
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
"requestEventNostrId": requestEvent.NostrId,
|
||||
"appId": app.ID,
|
||||
"bolt11": bolt11,
|
||||
}).Infof("Failed to send payment: %v", err)
|
||||
svc.EventPublisher.Publish(&events.Event{
|
||||
svc.eventPublisher.Publish(&events.Event{
|
||||
Event: "nwc_payment_failed",
|
||||
Properties: map[string]interface{}{
|
||||
// "error": fmt.Sprintf("%v", err),
|
||||
|
|
@ -82,9 +83,9 @@ func (svc *Service) HandlePayInvoiceEvent(ctx context.Context, nip47Request *Nip
|
|||
"amount": paymentRequest.MSatoshi / 1000,
|
||||
},
|
||||
})
|
||||
publishResponse(&Nip47Response{
|
||||
publishResponse(&nip47.Response{
|
||||
ResultType: nip47Request.Method,
|
||||
Error: &Nip47Error{
|
||||
Error: &nip47.Error{
|
||||
Code: nip47.ERROR_INTERNAL,
|
||||
Message: err.Error(),
|
||||
},
|
||||
|
|
@ -95,7 +96,7 @@ func (svc *Service) HandlePayInvoiceEvent(ctx context.Context, nip47Request *Nip
|
|||
// TODO: save payment fee
|
||||
svc.db.Save(&payment)
|
||||
|
||||
svc.EventPublisher.Publish(&events.Event{
|
||||
svc.eventPublisher.Publish(&events.Event{
|
||||
Event: "nwc_payment_succeeded",
|
||||
Properties: map[string]interface{}{
|
||||
"bolt11": bolt11,
|
||||
|
|
@ -103,9 +104,9 @@ func (svc *Service) HandlePayInvoiceEvent(ctx context.Context, nip47Request *Nip
|
|||
},
|
||||
})
|
||||
|
||||
publishResponse(&Nip47Response{
|
||||
publishResponse(&nip47.Response{
|
||||
ResultType: nip47Request.Method,
|
||||
Result: Nip47PayResponse{
|
||||
Result: nip47.PayResponse{
|
||||
Preimage: response.Preimage,
|
||||
FeesPaid: response.Fee,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -3,13 +3,14 @@ package main
|
|||
import (
|
||||
"context"
|
||||
|
||||
"github.com/getAlby/nostr-wallet-connect/db"
|
||||
"github.com/getAlby/nostr-wallet-connect/nip47"
|
||||
"github.com/nbd-wtf/go-nostr"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func (svc *Service) HandleSignMessageEvent(ctx context.Context, nip47Request *Nip47Request, requestEvent *RequestEvent, app *App, publishResponse func(*Nip47Response, nostr.Tags)) {
|
||||
signParams := &Nip47SignMessageParams{}
|
||||
func (svc *Service) HandleSignMessageEvent(ctx context.Context, nip47Request *nip47.Request, requestEvent *db.RequestEvent, app *db.App, publishResponse func(*nip47.Response, nostr.Tags)) {
|
||||
signParams := &nip47.SignMessageParams{}
|
||||
resp := svc.decodeNip47Request(nip47Request, requestEvent, app, signParams)
|
||||
if resp != nil {
|
||||
publishResponse(resp, nostr.Tags{})
|
||||
|
|
@ -22,20 +23,20 @@ func (svc *Service) HandleSignMessageEvent(ctx context.Context, nip47Request *Ni
|
|||
return
|
||||
}
|
||||
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
"requestEventNostrId": requestEvent.NostrId,
|
||||
"appId": app.ID,
|
||||
}).Info("Signing message")
|
||||
|
||||
signature, err := svc.lnClient.SignMessage(ctx, signParams.Message)
|
||||
if err != nil {
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
"requestEventNostrId": requestEvent.NostrId,
|
||||
"appId": app.ID,
|
||||
}).Infof("Failed to sign message: %v", err)
|
||||
publishResponse(&Nip47Response{
|
||||
publishResponse(&nip47.Response{
|
||||
ResultType: nip47Request.Method,
|
||||
Error: &Nip47Error{
|
||||
Error: &nip47.Error{
|
||||
Code: nip47.ERROR_INTERNAL,
|
||||
Message: err.Error(),
|
||||
},
|
||||
|
|
@ -43,12 +44,12 @@ func (svc *Service) HandleSignMessageEvent(ctx context.Context, nip47Request *Ni
|
|||
return
|
||||
}
|
||||
|
||||
responsePayload := Nip47SignMessageResponse{
|
||||
responsePayload := nip47.SignMessageResponse{
|
||||
Message: signParams.Message,
|
||||
Signature: signature,
|
||||
}
|
||||
|
||||
publishResponse(&Nip47Response{
|
||||
publishResponse(&nip47.Response{
|
||||
ResultType: nip47Request.Method,
|
||||
Result: responsePayload,
|
||||
}, nostr.Tags{})
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package http
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
|
@ -12,20 +12,27 @@ import (
|
|||
"github.com/labstack/echo-contrib/session"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/labstack/echo/v4/middleware"
|
||||
"github.com/sirupsen/logrus"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/getAlby/nostr-wallet-connect/alby"
|
||||
"github.com/getAlby/nostr-wallet-connect/config"
|
||||
"github.com/getAlby/nostr-wallet-connect/db"
|
||||
"github.com/getAlby/nostr-wallet-connect/events"
|
||||
models "github.com/getAlby/nostr-wallet-connect/models/http"
|
||||
"github.com/getAlby/nostr-wallet-connect/lsp"
|
||||
"github.com/getAlby/nostr-wallet-connect/service"
|
||||
|
||||
"github.com/getAlby/nostr-wallet-connect/api"
|
||||
"github.com/getAlby/nostr-wallet-connect/frontend"
|
||||
"github.com/getAlby/nostr-wallet-connect/models/api"
|
||||
)
|
||||
|
||||
type HttpService struct {
|
||||
svc *Service
|
||||
api *API
|
||||
albyHttpSvc *alby.AlbyHttpService
|
||||
api api.API
|
||||
albyHttpSvc *alby.AlbyHttpService
|
||||
cfg config.Config
|
||||
db *gorm.DB
|
||||
logger *logrus.Logger
|
||||
eventPublisher events.EventPublisher
|
||||
}
|
||||
|
||||
const (
|
||||
|
|
@ -33,11 +40,15 @@ const (
|
|||
sessionCookieAuthKey = "authenticated"
|
||||
)
|
||||
|
||||
func NewHttpService(svc *Service) *HttpService {
|
||||
func NewHttpService(svc service.Service, logger *logrus.Logger, db *gorm.DB, eventPublisher events.EventPublisher) *HttpService {
|
||||
|
||||
return &HttpService{
|
||||
svc: svc,
|
||||
api: NewAPI(svc),
|
||||
albyHttpSvc: alby.NewAlbyHttpService(svc.AlbyOAuthSvc, svc.Logger),
|
||||
api: api.NewAPI(svc, logger, db),
|
||||
albyHttpSvc: alby.NewAlbyHttpService(svc.GetAlbyOAuthSvc(), logger, svc.GetConfig().GetEnv()),
|
||||
cfg: svc.GetConfig(),
|
||||
db: db,
|
||||
logger: logger,
|
||||
eventPublisher: eventPublisher,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -58,7 +69,7 @@ func (httpSvc *HttpService) RegisterSharedRoutes(e *echo.Echo) {
|
|||
e.Use(middleware.CSRFWithConfig(middleware.CSRFConfig{
|
||||
TokenLookup: "header:X-CSRF-Token",
|
||||
}))
|
||||
e.Use(session.Middleware(sessions.NewCookieStore([]byte(httpSvc.svc.cfg.CookieSecret))))
|
||||
e.Use(session.Middleware(sessions.NewCookieStore([]byte(httpSvc.cfg.GetCookieSecret()))))
|
||||
|
||||
authMiddleware := httpSvc.validateUserMiddleware
|
||||
e.GET("/api/apps", httpSvc.appsListHandler, authMiddleware)
|
||||
|
|
@ -117,7 +128,7 @@ func (httpSvc *HttpService) RegisterSharedRoutes(e *echo.Echo) {
|
|||
func (httpSvc *HttpService) csrfHandler(c echo.Context) error {
|
||||
csrf, _ := c.Get(middleware.DefaultCSRFConfig.ContextKey).(string)
|
||||
if csrf == "" {
|
||||
return c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, ErrorResponse{
|
||||
Message: "CSRF token not available",
|
||||
})
|
||||
}
|
||||
|
|
@ -127,7 +138,7 @@ func (httpSvc *HttpService) csrfHandler(c echo.Context) error {
|
|||
func (httpSvc *HttpService) infoHandler(c echo.Context) error {
|
||||
responseBody, err := httpSvc.api.GetInfo(c.Request().Context())
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, ErrorResponse{
|
||||
Message: err.Error(),
|
||||
})
|
||||
}
|
||||
|
|
@ -143,14 +154,14 @@ func (httpSvc *HttpService) encryptedMnemonicHandler(c echo.Context) error {
|
|||
func (httpSvc *HttpService) backupReminderHandler(c echo.Context) error {
|
||||
var backupReminderRequest api.BackupReminderRequest
|
||||
if err := c.Bind(&backupReminderRequest); err != nil {
|
||||
return c.JSON(http.StatusBadRequest, models.ErrorResponse{
|
||||
return c.JSON(http.StatusBadRequest, ErrorResponse{
|
||||
Message: fmt.Sprintf("Bad request: %s", err.Error()),
|
||||
})
|
||||
}
|
||||
|
||||
err := httpSvc.api.SetNextBackupReminder(&backupReminderRequest)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, ErrorResponse{
|
||||
Message: fmt.Sprintf("Failed to store backup reminder: %s", err.Error()),
|
||||
})
|
||||
}
|
||||
|
|
@ -161,14 +172,14 @@ func (httpSvc *HttpService) backupReminderHandler(c echo.Context) error {
|
|||
func (httpSvc *HttpService) startHandler(c echo.Context) error {
|
||||
var startRequest api.StartRequest
|
||||
if err := c.Bind(&startRequest); err != nil {
|
||||
return c.JSON(http.StatusBadRequest, models.ErrorResponse{
|
||||
return c.JSON(http.StatusBadRequest, ErrorResponse{
|
||||
Message: fmt.Sprintf("Bad request: %s", err.Error()),
|
||||
})
|
||||
}
|
||||
|
||||
err := httpSvc.api.Start(&startRequest)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, ErrorResponse{
|
||||
Message: fmt.Sprintf("Failed to start node: %s", err.Error()),
|
||||
})
|
||||
}
|
||||
|
|
@ -176,7 +187,7 @@ func (httpSvc *HttpService) startHandler(c echo.Context) error {
|
|||
err = httpSvc.saveSessionCookie(c)
|
||||
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, ErrorResponse{
|
||||
Message: fmt.Sprintf("Failed to save session: %s", err.Error()),
|
||||
})
|
||||
}
|
||||
|
|
@ -187,13 +198,13 @@ func (httpSvc *HttpService) startHandler(c echo.Context) error {
|
|||
func (httpSvc *HttpService) unlockHandler(c echo.Context) error {
|
||||
var unlockRequest api.UnlockRequest
|
||||
if err := c.Bind(&unlockRequest); err != nil {
|
||||
return c.JSON(http.StatusBadRequest, models.ErrorResponse{
|
||||
return c.JSON(http.StatusBadRequest, ErrorResponse{
|
||||
Message: fmt.Sprintf("Bad request: %s", err.Error()),
|
||||
})
|
||||
}
|
||||
|
||||
if !httpSvc.svc.cfg.CheckUnlockPassword(unlockRequest.UnlockPassword) {
|
||||
return c.JSON(http.StatusUnauthorized, models.ErrorResponse{
|
||||
if !httpSvc.cfg.CheckUnlockPassword(unlockRequest.UnlockPassword) {
|
||||
return c.JSON(http.StatusUnauthorized, ErrorResponse{
|
||||
Message: "Invalid password",
|
||||
})
|
||||
}
|
||||
|
|
@ -201,12 +212,12 @@ func (httpSvc *HttpService) unlockHandler(c echo.Context) error {
|
|||
err := httpSvc.saveSessionCookie(c)
|
||||
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, ErrorResponse{
|
||||
Message: fmt.Sprintf("Failed to save session: %s", err.Error()),
|
||||
})
|
||||
}
|
||||
|
||||
httpSvc.svc.EventPublisher.Publish(&events.Event{
|
||||
httpSvc.eventPublisher.Publish(&events.Event{
|
||||
Event: "nwc_unlocked",
|
||||
})
|
||||
|
||||
|
|
@ -216,14 +227,14 @@ func (httpSvc *HttpService) unlockHandler(c echo.Context) error {
|
|||
func (httpSvc *HttpService) changeUnlockPasswordHandler(c echo.Context) error {
|
||||
var changeUnlockPasswordRequest api.ChangeUnlockPasswordRequest
|
||||
if err := c.Bind(&changeUnlockPasswordRequest); err != nil {
|
||||
return c.JSON(http.StatusBadRequest, models.ErrorResponse{
|
||||
return c.JSON(http.StatusBadRequest, ErrorResponse{
|
||||
Message: fmt.Sprintf("Bad request: %s", err.Error()),
|
||||
})
|
||||
}
|
||||
|
||||
err := httpSvc.api.ChangeUnlockPassword(&changeUnlockPasswordRequest)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, ErrorResponse{
|
||||
Message: fmt.Sprintf("Failed to change unlock password: %s", err.Error()),
|
||||
})
|
||||
}
|
||||
|
|
@ -246,7 +257,7 @@ func (httpSvc *HttpService) saveSessionCookie(c echo.Context) error {
|
|||
sess.Values[sessionCookieAuthKey] = true
|
||||
err := sess.Save(c.Request(), c.Response())
|
||||
if err != nil {
|
||||
httpSvc.svc.Logger.WithError(err).Error("Failed to save session")
|
||||
httpSvc.logger.WithError(err).Error("Failed to save session")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
|
@ -254,13 +265,13 @@ func (httpSvc *HttpService) saveSessionCookie(c echo.Context) error {
|
|||
func (httpSvc *HttpService) logoutHandler(c echo.Context) error {
|
||||
sess, err := session.Get(sessionCookieName, c)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, ErrorResponse{
|
||||
Message: "Failed to get session",
|
||||
})
|
||||
}
|
||||
sess.Options.MaxAge = -1
|
||||
if err := sess.Save(c.Request(), c.Response()); err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, ErrorResponse{
|
||||
Message: "Failed to save session",
|
||||
})
|
||||
}
|
||||
|
|
@ -273,7 +284,7 @@ func (httpSvc *HttpService) channelsListHandler(c echo.Context) error {
|
|||
channels, err := httpSvc.api.ListChannels(ctx)
|
||||
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, ErrorResponse{
|
||||
Message: err.Error(),
|
||||
})
|
||||
}
|
||||
|
|
@ -287,7 +298,7 @@ func (httpSvc *HttpService) channelPeerSuggestionsHandler(c echo.Context) error
|
|||
suggestions, err := httpSvc.api.GetChannelPeerSuggestions(ctx)
|
||||
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, ErrorResponse{
|
||||
Message: err.Error(),
|
||||
})
|
||||
}
|
||||
|
|
@ -298,15 +309,15 @@ func (httpSvc *HttpService) channelPeerSuggestionsHandler(c echo.Context) error
|
|||
func (httpSvc *HttpService) resetRouterHandler(c echo.Context) error {
|
||||
var resetRouterRequest api.ResetRouterRequest
|
||||
if err := c.Bind(&resetRouterRequest); err != nil {
|
||||
return c.JSON(http.StatusBadRequest, models.ErrorResponse{
|
||||
return c.JSON(http.StatusBadRequest, ErrorResponse{
|
||||
Message: fmt.Sprintf("Bad request: %s", err.Error()),
|
||||
})
|
||||
}
|
||||
|
||||
err := httpSvc.api.ResetRouter(resetRouterRequest.Key, true)
|
||||
err := httpSvc.api.ResetRouter(resetRouterRequest.Key)
|
||||
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, ErrorResponse{
|
||||
Message: err.Error(),
|
||||
})
|
||||
}
|
||||
|
|
@ -319,7 +330,7 @@ func (httpSvc *HttpService) stopHandler(c echo.Context) error {
|
|||
err := httpSvc.api.Stop()
|
||||
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, ErrorResponse{
|
||||
Message: err.Error(),
|
||||
})
|
||||
}
|
||||
|
|
@ -333,7 +344,7 @@ func (httpSvc *HttpService) nodeConnectionInfoHandler(c echo.Context) error {
|
|||
info, err := httpSvc.api.GetNodeConnectionInfo(ctx)
|
||||
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, ErrorResponse{
|
||||
Message: err.Error(),
|
||||
})
|
||||
}
|
||||
|
|
@ -347,7 +358,7 @@ func (httpSvc *HttpService) nodeStatusHandler(c echo.Context) error {
|
|||
info, err := httpSvc.api.GetNodeStatus(ctx)
|
||||
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, ErrorResponse{
|
||||
Message: err.Error(),
|
||||
})
|
||||
}
|
||||
|
|
@ -361,7 +372,7 @@ func (httpSvc *HttpService) nodeNetworkGraphHandler(c echo.Context) error {
|
|||
info, err := httpSvc.api.GetNetworkGraph(nodeIds)
|
||||
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, ErrorResponse{
|
||||
Message: err.Error(),
|
||||
})
|
||||
}
|
||||
|
|
@ -375,7 +386,7 @@ func (httpSvc *HttpService) balancesHandler(c echo.Context) error {
|
|||
balances, err := httpSvc.api.GetBalances(ctx)
|
||||
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, ErrorResponse{
|
||||
Message: err.Error(),
|
||||
})
|
||||
}
|
||||
|
|
@ -392,15 +403,15 @@ func (httpSvc *HttpService) walletSyncHandler(c echo.Context) error {
|
|||
func (httpSvc *HttpService) mempoolApiHandler(c echo.Context) error {
|
||||
endpoint := c.QueryParam("endpoint")
|
||||
if endpoint == "" {
|
||||
return c.JSON(http.StatusBadRequest, models.ErrorResponse{
|
||||
return c.JSON(http.StatusBadRequest, ErrorResponse{
|
||||
Message: "Invalid pubkey parameter",
|
||||
})
|
||||
}
|
||||
|
||||
response, err := httpSvc.api.RequestMempoolApi(endpoint)
|
||||
if err != nil {
|
||||
httpSvc.svc.Logger.WithField("endpoint", endpoint).WithError(err).Error("Failed to request mempool API")
|
||||
return c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
||||
httpSvc.logger.WithField("endpoint", endpoint).WithError(err).Error("Failed to request mempool API")
|
||||
return c.JSON(http.StatusInternalServerError, ErrorResponse{
|
||||
Message: fmt.Sprintf("Failed to request mempool API: %s", err.Error()),
|
||||
})
|
||||
}
|
||||
|
|
@ -409,9 +420,9 @@ func (httpSvc *HttpService) mempoolApiHandler(c echo.Context) error {
|
|||
}
|
||||
|
||||
func (httpSvc *HttpService) listPeers(c echo.Context) error {
|
||||
peers, err := httpSvc.api.ListPeers(httpSvc.svc.ctx)
|
||||
peers, err := httpSvc.api.ListPeers(c.Request().Context())
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, ErrorResponse{
|
||||
Message: fmt.Sprintf("Failed to list peers: %s", err.Error()),
|
||||
})
|
||||
}
|
||||
|
|
@ -424,7 +435,7 @@ func (httpSvc *HttpService) connectPeerHandler(c echo.Context) error {
|
|||
|
||||
var connectPeerRequest api.ConnectPeerRequest
|
||||
if err := c.Bind(&connectPeerRequest); err != nil {
|
||||
return c.JSON(http.StatusBadRequest, models.ErrorResponse{
|
||||
return c.JSON(http.StatusBadRequest, ErrorResponse{
|
||||
Message: fmt.Sprintf("Bad request: %s", err.Error()),
|
||||
})
|
||||
}
|
||||
|
|
@ -432,7 +443,7 @@ func (httpSvc *HttpService) connectPeerHandler(c echo.Context) error {
|
|||
err := httpSvc.api.ConnectPeer(ctx, &connectPeerRequest)
|
||||
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, ErrorResponse{
|
||||
Message: fmt.Sprintf("Failed to connect peer: %s", err.Error()),
|
||||
})
|
||||
}
|
||||
|
|
@ -445,7 +456,7 @@ func (httpSvc *HttpService) openChannelHandler(c echo.Context) error {
|
|||
|
||||
var openChannelRequest api.OpenChannelRequest
|
||||
if err := c.Bind(&openChannelRequest); err != nil {
|
||||
return c.JSON(http.StatusBadRequest, models.ErrorResponse{
|
||||
return c.JSON(http.StatusBadRequest, ErrorResponse{
|
||||
Message: fmt.Sprintf("Bad request: %s", err.Error()),
|
||||
})
|
||||
}
|
||||
|
|
@ -453,7 +464,7 @@ func (httpSvc *HttpService) openChannelHandler(c echo.Context) error {
|
|||
openChannelResponse, err := httpSvc.api.OpenChannel(ctx, &openChannelRequest)
|
||||
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, ErrorResponse{
|
||||
Message: fmt.Sprintf("Failed to open channel: %s", err.Error()),
|
||||
})
|
||||
}
|
||||
|
|
@ -467,7 +478,7 @@ func (httpSvc *HttpService) closeChannelHandler(c echo.Context) error {
|
|||
closeChannelResponse, err := httpSvc.api.CloseChannel(ctx, c.Param("peerId"), c.Param("channelId"), c.QueryParam("force") == "true")
|
||||
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, ErrorResponse{
|
||||
Message: fmt.Sprintf("Failed to close channel: %s", err.Error()),
|
||||
})
|
||||
}
|
||||
|
|
@ -478,17 +489,17 @@ func (httpSvc *HttpService) closeChannelHandler(c echo.Context) error {
|
|||
func (httpSvc *HttpService) newInstantChannelInvoiceHandler(c echo.Context) error {
|
||||
ctx := c.Request().Context()
|
||||
|
||||
var newWrappedInvoiceRequest api.NewInstantChannelInvoiceRequest
|
||||
var newWrappedInvoiceRequest lsp.NewInstantChannelInvoiceRequest
|
||||
if err := c.Bind(&newWrappedInvoiceRequest); err != nil {
|
||||
return c.JSON(http.StatusBadRequest, models.ErrorResponse{
|
||||
return c.JSON(http.StatusBadRequest, ErrorResponse{
|
||||
Message: fmt.Sprintf("Bad request: %s", err.Error()),
|
||||
})
|
||||
}
|
||||
|
||||
newWrappedInvoiceResponse, err := httpSvc.api.NewInstantChannelInvoice(ctx, &newWrappedInvoiceRequest)
|
||||
newWrappedInvoiceResponse, err := httpSvc.api.GetLSPService().NewInstantChannelInvoice(ctx, &newWrappedInvoiceRequest)
|
||||
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, ErrorResponse{
|
||||
Message: fmt.Sprintf("Failed to request wrapped invoice: %s", err.Error()),
|
||||
})
|
||||
}
|
||||
|
|
@ -502,7 +513,7 @@ func (httpSvc *HttpService) newOnchainAddressHandler(c echo.Context) error {
|
|||
newAddressResponse, err := httpSvc.api.GetNewOnchainAddress(ctx)
|
||||
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, ErrorResponse{
|
||||
Message: fmt.Sprintf("Failed to request new onchain address: %s", err.Error()),
|
||||
})
|
||||
}
|
||||
|
|
@ -515,7 +526,7 @@ func (httpSvc *HttpService) redeemOnchainFundsHandler(c echo.Context) error {
|
|||
|
||||
var redeemOnchainFundsRequest api.RedeemOnchainFundsRequest
|
||||
if err := c.Bind(&redeemOnchainFundsRequest); err != nil {
|
||||
return c.JSON(http.StatusBadRequest, models.ErrorResponse{
|
||||
return c.JSON(http.StatusBadRequest, ErrorResponse{
|
||||
Message: fmt.Sprintf("Bad request: %s", err.Error()),
|
||||
})
|
||||
}
|
||||
|
|
@ -523,7 +534,7 @@ func (httpSvc *HttpService) redeemOnchainFundsHandler(c echo.Context) error {
|
|||
redeemOnchainFundsResponse, err := httpSvc.api.RedeemOnchainFunds(ctx, redeemOnchainFundsRequest.ToAddress)
|
||||
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, ErrorResponse{
|
||||
Message: fmt.Sprintf("Failed to redeem onchain funds: %s", err.Error()),
|
||||
})
|
||||
}
|
||||
|
|
@ -536,7 +547,7 @@ func (httpSvc *HttpService) signMessageHandler(c echo.Context) error {
|
|||
|
||||
var signMessageRequest api.SignMessageRequest
|
||||
if err := c.Bind(&signMessageRequest); err != nil {
|
||||
return c.JSON(http.StatusBadRequest, models.ErrorResponse{
|
||||
return c.JSON(http.StatusBadRequest, ErrorResponse{
|
||||
Message: fmt.Sprintf("Bad request: %s", err.Error()),
|
||||
})
|
||||
}
|
||||
|
|
@ -544,7 +555,7 @@ func (httpSvc *HttpService) signMessageHandler(c echo.Context) error {
|
|||
signMessageResponse, err := httpSvc.api.SignMessage(ctx, signMessageRequest.Message)
|
||||
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, ErrorResponse{
|
||||
Message: fmt.Sprintf("Failed to sign messae: %s", err.Error()),
|
||||
})
|
||||
}
|
||||
|
|
@ -555,7 +566,7 @@ func (httpSvc *HttpService) appsListHandler(c echo.Context) error {
|
|||
apps, err := httpSvc.api.ListApps()
|
||||
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, ErrorResponse{
|
||||
Message: err.Error(),
|
||||
})
|
||||
}
|
||||
|
|
@ -564,11 +575,11 @@ func (httpSvc *HttpService) appsListHandler(c echo.Context) error {
|
|||
}
|
||||
|
||||
func (httpSvc *HttpService) appsShowHandler(c echo.Context) error {
|
||||
app := App{}
|
||||
findResult := httpSvc.svc.db.Where("nostr_pubkey = ?", c.Param("pubkey")).First(&app)
|
||||
app := db.App{}
|
||||
findResult := httpSvc.db.Where("nostr_pubkey = ?", c.Param("pubkey")).First(&app)
|
||||
|
||||
if findResult.RowsAffected == 0 {
|
||||
return c.JSON(http.StatusNotFound, models.ErrorResponse{
|
||||
return c.JSON(http.StatusNotFound, ErrorResponse{
|
||||
Message: "App does not exist",
|
||||
})
|
||||
}
|
||||
|
|
@ -581,16 +592,16 @@ func (httpSvc *HttpService) appsShowHandler(c echo.Context) error {
|
|||
func (httpSvc *HttpService) appsUpdateHandler(c echo.Context) error {
|
||||
var requestData api.UpdateAppRequest
|
||||
if err := c.Bind(&requestData); err != nil {
|
||||
return c.JSON(http.StatusBadRequest, models.ErrorResponse{
|
||||
return c.JSON(http.StatusBadRequest, ErrorResponse{
|
||||
Message: fmt.Sprintf("Bad request: %s", err.Error()),
|
||||
})
|
||||
}
|
||||
|
||||
app := App{}
|
||||
findResult := httpSvc.svc.db.Where("nostr_pubkey = ?", c.Param("pubkey")).First(&app)
|
||||
app := db.App{}
|
||||
findResult := httpSvc.db.Where("nostr_pubkey = ?", c.Param("pubkey")).First(&app)
|
||||
|
||||
if findResult.RowsAffected == 0 {
|
||||
return c.JSON(http.StatusNotFound, models.ErrorResponse{
|
||||
return c.JSON(http.StatusNotFound, ErrorResponse{
|
||||
Message: "App does not exist",
|
||||
})
|
||||
}
|
||||
|
|
@ -598,8 +609,8 @@ func (httpSvc *HttpService) appsUpdateHandler(c echo.Context) error {
|
|||
err := httpSvc.api.UpdateApp(&app, &requestData)
|
||||
|
||||
if err != nil {
|
||||
httpSvc.svc.Logger.WithError(err).Error("Failed to update app")
|
||||
return c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
||||
httpSvc.logger.WithError(err).Error("Failed to update app")
|
||||
return c.JSON(http.StatusInternalServerError, ErrorResponse{
|
||||
Message: fmt.Sprintf("Failed to update app: %v", err),
|
||||
})
|
||||
}
|
||||
|
|
@ -610,25 +621,25 @@ func (httpSvc *HttpService) appsUpdateHandler(c echo.Context) error {
|
|||
func (httpSvc *HttpService) appsDeleteHandler(c echo.Context) error {
|
||||
pubkey := c.Param("pubkey")
|
||||
if pubkey == "" {
|
||||
return c.JSON(http.StatusBadRequest, models.ErrorResponse{
|
||||
return c.JSON(http.StatusBadRequest, ErrorResponse{
|
||||
Message: "Invalid pubkey parameter",
|
||||
})
|
||||
}
|
||||
app := App{}
|
||||
result := httpSvc.svc.db.Where("nostr_pubkey = ?", pubkey).First(&app)
|
||||
app := db.App{}
|
||||
result := httpSvc.db.Where("nostr_pubkey = ?", pubkey).First(&app)
|
||||
if result.Error != nil {
|
||||
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
|
||||
return c.JSON(http.StatusNotFound, models.ErrorResponse{
|
||||
return c.JSON(http.StatusNotFound, ErrorResponse{
|
||||
Message: "App not found",
|
||||
})
|
||||
}
|
||||
return c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, ErrorResponse{
|
||||
Message: "Failed to fetch app",
|
||||
})
|
||||
}
|
||||
|
||||
if err := httpSvc.api.DeleteApp(&app); err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, ErrorResponse{
|
||||
Message: "Failed to delete app",
|
||||
})
|
||||
}
|
||||
|
|
@ -638,7 +649,7 @@ func (httpSvc *HttpService) appsDeleteHandler(c echo.Context) error {
|
|||
func (httpSvc *HttpService) appsCreateHandler(c echo.Context) error {
|
||||
var requestData api.CreateAppRequest
|
||||
if err := c.Bind(&requestData); err != nil {
|
||||
return c.JSON(http.StatusBadRequest, models.ErrorResponse{
|
||||
return c.JSON(http.StatusBadRequest, ErrorResponse{
|
||||
Message: fmt.Sprintf("Bad request: %s", err.Error()),
|
||||
})
|
||||
}
|
||||
|
|
@ -646,8 +657,8 @@ func (httpSvc *HttpService) appsCreateHandler(c echo.Context) error {
|
|||
responseBody, err := httpSvc.api.CreateApp(&requestData)
|
||||
|
||||
if err != nil {
|
||||
httpSvc.svc.Logger.WithField("requestData", requestData).WithError(err).Error("Failed to save app")
|
||||
return c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
||||
httpSvc.logger.WithField("requestData", requestData).WithError(err).Error("Failed to save app")
|
||||
return c.JSON(http.StatusInternalServerError, ErrorResponse{
|
||||
Message: fmt.Sprintf("Failed to save app: %v", err),
|
||||
})
|
||||
}
|
||||
|
|
@ -658,14 +669,14 @@ func (httpSvc *HttpService) appsCreateHandler(c echo.Context) error {
|
|||
func (httpSvc *HttpService) setupHandler(c echo.Context) error {
|
||||
var setupRequest api.SetupRequest
|
||||
if err := c.Bind(&setupRequest); err != nil {
|
||||
return c.JSON(http.StatusBadRequest, models.ErrorResponse{
|
||||
return c.JSON(http.StatusBadRequest, ErrorResponse{
|
||||
Message: fmt.Sprintf("Bad request: %s", err.Error()),
|
||||
})
|
||||
}
|
||||
|
||||
err := httpSvc.api.Setup(c.Request().Context(), &setupRequest)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, ErrorResponse{
|
||||
Message: fmt.Sprintf("Failed to setup node: %s", err.Error()),
|
||||
})
|
||||
}
|
||||
|
|
@ -676,14 +687,14 @@ func (httpSvc *HttpService) setupHandler(c echo.Context) error {
|
|||
func (httpSvc *HttpService) sendPaymentProbesHandler(c echo.Context) error {
|
||||
var sendPaymentProbesRequest api.SendPaymentProbesRequest
|
||||
if err := c.Bind(&sendPaymentProbesRequest); err != nil {
|
||||
return c.JSON(http.StatusBadRequest, models.ErrorResponse{
|
||||
return c.JSON(http.StatusBadRequest, ErrorResponse{
|
||||
Message: fmt.Sprintf("Bad request: %s", err.Error()),
|
||||
})
|
||||
}
|
||||
|
||||
sendPaymentProbesResponse, err := httpSvc.api.SendPaymentProbes(c.Request().Context(), &sendPaymentProbesRequest)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, ErrorResponse{
|
||||
Message: fmt.Sprintf("Failed to send payment probes: %v", err),
|
||||
})
|
||||
}
|
||||
|
|
@ -694,14 +705,14 @@ func (httpSvc *HttpService) sendPaymentProbesHandler(c echo.Context) error {
|
|||
func (httpSvc *HttpService) sendSpontaneousPaymentProbesHandler(c echo.Context) error {
|
||||
var sendSpontaneousPaymentProbesRequest api.SendSpontaneousPaymentProbesRequest
|
||||
if err := c.Bind(&sendSpontaneousPaymentProbesRequest); err != nil {
|
||||
return c.JSON(http.StatusBadRequest, models.ErrorResponse{
|
||||
return c.JSON(http.StatusBadRequest, ErrorResponse{
|
||||
Message: fmt.Sprintf("Bad request: %s", err.Error()),
|
||||
})
|
||||
}
|
||||
|
||||
sendSpontaneousPaymentProbesResponse, err := httpSvc.api.SendSpontaneousPaymentProbes(c.Request().Context(), &sendSpontaneousPaymentProbesRequest)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, ErrorResponse{
|
||||
Message: fmt.Sprintf("Failed to send spontaneous payment probes: %v", err),
|
||||
})
|
||||
}
|
||||
|
|
@ -712,21 +723,21 @@ func (httpSvc *HttpService) sendSpontaneousPaymentProbesHandler(c echo.Context)
|
|||
func (httpSvc *HttpService) getLogOutputHandler(c echo.Context) error {
|
||||
var getLogRequest api.GetLogOutputRequest
|
||||
if err := c.Bind(&getLogRequest); err != nil {
|
||||
return c.JSON(http.StatusBadRequest, models.ErrorResponse{
|
||||
return c.JSON(http.StatusBadRequest, ErrorResponse{
|
||||
Message: fmt.Sprintf("Bad request: %s", err.Error()),
|
||||
})
|
||||
}
|
||||
|
||||
logType := c.Param("type")
|
||||
if logType != api.LogTypeNode && logType != api.LogTypeApp {
|
||||
return c.JSON(http.StatusBadRequest, models.ErrorResponse{
|
||||
return c.JSON(http.StatusBadRequest, ErrorResponse{
|
||||
Message: fmt.Sprintf("Invalid log type parameter: '%s'", logType),
|
||||
})
|
||||
}
|
||||
|
||||
getLogResponse, err := httpSvc.api.GetLogOutput(c.Request().Context(), logType, &getLogRequest)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, ErrorResponse{
|
||||
Message: fmt.Sprintf("Failed to get log output: %v", err),
|
||||
})
|
||||
}
|
||||
|
|
@ -737,19 +748,19 @@ func (httpSvc *HttpService) getLogOutputHandler(c echo.Context) error {
|
|||
func (httpSvc *HttpService) createBackupHandler(c echo.Context) error {
|
||||
var backupRequest api.BasicBackupRequest
|
||||
if err := c.Bind(&backupRequest); err != nil {
|
||||
return c.JSON(http.StatusBadRequest, models.ErrorResponse{
|
||||
return c.JSON(http.StatusBadRequest, ErrorResponse{
|
||||
Message: fmt.Sprintf("Bad request: %s", err.Error()),
|
||||
})
|
||||
}
|
||||
|
||||
if !httpSvc.svc.cfg.CheckUnlockPassword(backupRequest.UnlockPassword) {
|
||||
return c.JSON(http.StatusUnauthorized, models.ErrorResponse{
|
||||
if !httpSvc.cfg.CheckUnlockPassword(backupRequest.UnlockPassword) {
|
||||
return c.JSON(http.StatusUnauthorized, ErrorResponse{
|
||||
Message: "Invalid password",
|
||||
})
|
||||
}
|
||||
|
||||
var buffer bytes.Buffer
|
||||
err := httpSvc.api.CreateBackup(&backupRequest, &buffer)
|
||||
err := httpSvc.api.GetBackupService().CreateBackup(backupRequest.UnlockPassword, &buffer)
|
||||
if err != nil {
|
||||
return c.String(500, fmt.Sprintf("Failed to create backup: %v", err))
|
||||
}
|
||||
|
|
@ -774,22 +785,22 @@ func (httpSvc *HttpService) restoreBackupHandler(c echo.Context) error {
|
|||
|
||||
fileHeader, err := c.FormFile("backup")
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusBadRequest, models.ErrorResponse{
|
||||
return c.JSON(http.StatusBadRequest, ErrorResponse{
|
||||
Message: fmt.Sprintf("Failed to get backup file header: %v", err),
|
||||
})
|
||||
}
|
||||
|
||||
file, err := fileHeader.Open()
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, ErrorResponse{
|
||||
Message: fmt.Sprintf("Failed to open backup file: %v", err),
|
||||
})
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
err = httpSvc.api.RestoreBackup(password, file)
|
||||
err = httpSvc.api.GetBackupService().RestoreBackup(password, file)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, ErrorResponse{
|
||||
Message: fmt.Sprintf("Failed to restore backup: %v", err),
|
||||
})
|
||||
}
|
||||
|
|
@ -1,4 +1,3 @@
|
|||
// TODO: move to http/models.go
|
||||
package http
|
||||
|
||||
type ErrorResponse struct {
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
//go:build !skip_breez
|
||||
|
||||
package main
|
||||
package breez
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
|
@ -16,7 +16,8 @@ import (
|
|||
decodepay "github.com/nbd-wtf/ln-decodepay"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/getAlby/nostr-wallet-connect/models/lnclient"
|
||||
"github.com/getAlby/nostr-wallet-connect/lnclient"
|
||||
"github.com/getAlby/nostr-wallet-connect/nip47"
|
||||
)
|
||||
|
||||
type BreezService struct {
|
||||
|
|
@ -102,7 +103,7 @@ func (bs *BreezService) Shutdown() error {
|
|||
return bs.svc.Disconnect()
|
||||
}
|
||||
|
||||
func (bs *BreezService) SendPaymentSync(ctx context.Context, payReq string) (*lnclient.Nip47PayInvoiceResponse, error) {
|
||||
func (bs *BreezService) SendPaymentSync(ctx context.Context, payReq string) (*lnclient.PayInvoiceResponse, error) {
|
||||
sendPaymentRequest := breez_sdk.SendPaymentRequest{
|
||||
Bolt11: payReq,
|
||||
}
|
||||
|
|
@ -114,7 +115,7 @@ func (bs *BreezService) SendPaymentSync(ctx context.Context, payReq string) (*ln
|
|||
if resp.Payment.Details != nil {
|
||||
lnDetails, _ = resp.Payment.Details.(breez_sdk.PaymentDetailsLn)
|
||||
}
|
||||
return &lnclient.Nip47PayInvoiceResponse{
|
||||
return &lnclient.PayInvoiceResponse{
|
||||
Preimage: lnDetails.Data.PaymentPreimage,
|
||||
}, nil
|
||||
|
||||
|
|
@ -153,7 +154,7 @@ func (bs *BreezService) GetBalance(ctx context.Context) (balance int64, err erro
|
|||
return int64(info.MaxPayableMsat), nil
|
||||
}
|
||||
|
||||
func (bs *BreezService) MakeInvoice(ctx context.Context, amount int64, description string, descriptionHash string, expiry int64) (transaction *Nip47Transaction, err error) {
|
||||
func (bs *BreezService) MakeInvoice(ctx context.Context, amount int64, description string, descriptionHash string, expiry int64) (transaction *nip47.Transaction, err error) {
|
||||
expiry32 := uint32(expiry)
|
||||
receivePaymentRequest := breez_sdk.ReceivePaymentRequest{
|
||||
// amount provided in msat
|
||||
|
|
@ -166,7 +167,7 @@ func (bs *BreezService) MakeInvoice(ctx context.Context, amount int64, descripti
|
|||
return nil, err
|
||||
}
|
||||
|
||||
tx := &Nip47Transaction{
|
||||
tx := &nip47.Transaction{
|
||||
Type: "incoming",
|
||||
Invoice: resp.LnInvoice.Bolt11,
|
||||
Preimage: hex.EncodeToString(resp.LnInvoice.PaymentSecret),
|
||||
|
|
@ -191,7 +192,7 @@ func (bs *BreezService) MakeInvoice(ctx context.Context, amount int64, descripti
|
|||
return tx, nil
|
||||
}
|
||||
|
||||
func (bs *BreezService) LookupInvoice(ctx context.Context, paymentHash string) (transaction *Nip47Transaction, err error) {
|
||||
func (bs *BreezService) LookupInvoice(ctx context.Context, paymentHash string) (transaction *nip47.Transaction, err error) {
|
||||
log.Printf("p: %v", paymentHash)
|
||||
payment, err := bs.svc.PaymentByHash(paymentHash)
|
||||
if err != nil {
|
||||
|
|
@ -209,7 +210,7 @@ func (bs *BreezService) LookupInvoice(ctx context.Context, paymentHash string) (
|
|||
}
|
||||
}
|
||||
|
||||
func (bs *BreezService) ListTransactions(ctx context.Context, from, until, limit, offset uint64, unpaid bool, invoiceType string) (transactions []Nip47Transaction, err error) {
|
||||
func (bs *BreezService) ListTransactions(ctx context.Context, from, until, limit, offset uint64, unpaid bool, invoiceType string) (transactions []nip47.Transaction, err error) {
|
||||
|
||||
request := breez_sdk.ListPaymentsRequest{}
|
||||
if limit > 0 {
|
||||
|
|
@ -234,7 +235,7 @@ func (bs *BreezService) ListTransactions(ctx context.Context, from, until, limit
|
|||
return nil, err
|
||||
}
|
||||
|
||||
transactions = []Nip47Transaction{}
|
||||
transactions = []nip47.Transaction{}
|
||||
for _, payment := range payments {
|
||||
if payment.PaymentType != breez_sdk.PaymentTypeReceived && payment.PaymentType != breez_sdk.PaymentTypeSent {
|
||||
// skip other types of payments for now
|
||||
|
|
@ -280,7 +281,7 @@ func (bs *BreezService) CloseChannel(ctx context.Context, closeChannelRequest *l
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
func breezPaymentToTransaction(payment *breez_sdk.Payment) (*Nip47Transaction, error) {
|
||||
func breezPaymentToTransaction(payment *breez_sdk.Payment) (*nip47.Transaction, error) {
|
||||
var lnDetails breez_sdk.PaymentDetailsLn
|
||||
if payment.Details != nil {
|
||||
lnDetails, _ = payment.Details.(breez_sdk.PaymentDetailsLn)
|
||||
|
|
@ -312,7 +313,7 @@ func breezPaymentToTransaction(payment *breez_sdk.Payment) (*Nip47Transaction, e
|
|||
descriptionHash = paymentRequest.DescriptionHash
|
||||
}
|
||||
|
||||
tx := &Nip47Transaction{
|
||||
tx := &nip47.Transaction{
|
||||
Type: txType,
|
||||
Invoice: lnDetails.Data.Bolt11,
|
||||
Preimage: lnDetails.Data.PaymentPreimage,
|
||||
|
|
@ -463,3 +464,5 @@ func (bs *BreezService) GetStorageDir() (string, error) {
|
|||
func (bs *BreezService) GetNetworkGraph(nodeIds []string) (lnclient.NetworkGraphResponse, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (bs *BreezService) UpdateLastWalletSyncRequest() {}
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
//go:build skip_breez
|
||||
|
||||
package main
|
||||
package breez
|
||||
|
||||
import (
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/getAlby/nostr-wallet-connect/models/lnclient"
|
||||
"github.com/getAlby/nostr-wallet-connect/lnclient"
|
||||
)
|
||||
|
||||
func NewBreezService(logger *logrus.Logger, mnemonic, apiKey, inviteCode, workDir string) (result lnclient.LNClient, err error) {
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package greenlight
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
|
@ -19,18 +19,20 @@ import (
|
|||
decodepay "github.com/nbd-wtf/ln-decodepay"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/getAlby/nostr-wallet-connect/models/lnclient"
|
||||
"github.com/getAlby/nostr-wallet-connect/config"
|
||||
"github.com/getAlby/nostr-wallet-connect/lnclient"
|
||||
"github.com/getAlby/nostr-wallet-connect/nip47"
|
||||
)
|
||||
|
||||
type GreenlightService struct {
|
||||
workdir string
|
||||
client *glalby.BlockingGreenlightAlbyClient
|
||||
svc *Service
|
||||
logger *logrus.Logger
|
||||
}
|
||||
|
||||
const DEVICE_CREDENTIALS_KEY = "GreenlightCreds"
|
||||
|
||||
func NewGreenlightService(svc *Service, mnemonic, inviteCode, workDir, encryptionKey string) (result lnclient.LNClient, err error) {
|
||||
func NewGreenlightService(cfg config.Config, logger *logrus.Logger, mnemonic, inviteCode, workDir, encryptionKey string) (result lnclient.LNClient, err error) {
|
||||
if mnemonic == "" || inviteCode == "" || workDir == "" {
|
||||
return nil, errors.New("one or more required greenlight configuration are missing")
|
||||
}
|
||||
|
|
@ -44,35 +46,35 @@ func NewGreenlightService(svc *Service, mnemonic, inviteCode, workDir, encryptio
|
|||
}
|
||||
|
||||
var credentials *glalby.GreenlightCredentials
|
||||
existingDeviceCreds, _ := svc.cfg.Get(DEVICE_CREDENTIALS_KEY, encryptionKey)
|
||||
existingDeviceCreds, _ := cfg.Get(DEVICE_CREDENTIALS_KEY, encryptionKey)
|
||||
|
||||
if existingDeviceCreds != "" {
|
||||
credentials = &glalby.GreenlightCredentials{
|
||||
GlCreds: existingDeviceCreds,
|
||||
}
|
||||
svc.Logger.Info("Using saved greenlight credentials")
|
||||
logger.Info("Using saved greenlight credentials")
|
||||
}
|
||||
|
||||
if credentials == nil {
|
||||
svc.Logger.Info("No greenlight credentials found, attempting to recover existing node")
|
||||
logger.Info("No greenlight credentials found, attempting to recover existing node")
|
||||
recoveredCredentials, err := glalby.Recover(mnemonic)
|
||||
credentials = &recoveredCredentials
|
||||
|
||||
if err != nil {
|
||||
svc.Logger.Errorf("Failed to recover node: %v", err)
|
||||
svc.Logger.Infof("Trying to register instead...")
|
||||
logger.Errorf("Failed to recover node: %v", err)
|
||||
logger.Infof("Trying to register instead...")
|
||||
recoveredCredentials, err := glalby.Register(mnemonic, inviteCode)
|
||||
credentials = &recoveredCredentials
|
||||
|
||||
if err != nil {
|
||||
svc.Logger.Fatalf("Failed to register new node")
|
||||
logger.Fatalf("Failed to register new node")
|
||||
}
|
||||
}
|
||||
|
||||
if credentials == nil || credentials.GlCreds == "" {
|
||||
return nil, errors.New("unexpected response from Recover")
|
||||
}
|
||||
svc.cfg.SetUpdate(DEVICE_CREDENTIALS_KEY, credentials.GlCreds, encryptionKey)
|
||||
cfg.SetUpdate(DEVICE_CREDENTIALS_KEY, credentials.GlCreds, encryptionKey)
|
||||
}
|
||||
|
||||
client, err := glalby.NewBlockingGreenlightAlbyClient(mnemonic, *credentials)
|
||||
|
|
@ -88,7 +90,7 @@ func NewGreenlightService(svc *Service, mnemonic, inviteCode, workDir, encryptio
|
|||
gs := GreenlightService{
|
||||
workdir: newpath,
|
||||
client: client,
|
||||
svc: svc,
|
||||
logger: logger,
|
||||
}
|
||||
|
||||
nodeInfo, err := client.GetInfo()
|
||||
|
|
@ -105,23 +107,23 @@ func NewGreenlightService(svc *Service, mnemonic, inviteCode, workDir, encryptio
|
|||
func (gs *GreenlightService) Shutdown() error {
|
||||
_, err := gs.client.Shutdown()
|
||||
if err != nil {
|
||||
gs.svc.Logger.WithError(err).Error("Failed to shutdown greenlight node")
|
||||
gs.logger.WithError(err).Error("Failed to shutdown greenlight node")
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gs *GreenlightService) SendPaymentSync(ctx context.Context, payReq string) (*lnclient.Nip47PayInvoiceResponse, error) {
|
||||
func (gs *GreenlightService) SendPaymentSync(ctx context.Context, payReq string) (*lnclient.PayInvoiceResponse, error) {
|
||||
response, err := gs.client.Pay(glalby.PayRequest{
|
||||
Bolt11: payReq,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
gs.svc.Logger.Errorf("Failed to send payment: %v", err)
|
||||
gs.logger.Errorf("Failed to send payment: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
log.Printf("SendPaymentSync succeeded: %v", response.Preimage)
|
||||
return &lnclient.Nip47PayInvoiceResponse{
|
||||
return &lnclient.PayInvoiceResponse{
|
||||
Preimage: response.Preimage,
|
||||
}, nil
|
||||
}
|
||||
|
|
@ -146,7 +148,7 @@ func (gs *GreenlightService) SendKeysend(ctx context.Context, amount int64, dest
|
|||
})
|
||||
|
||||
if err != nil {
|
||||
gs.svc.Logger.Errorf("Failed to send keysend payment: %v", err)
|
||||
gs.logger.Errorf("Failed to send keysend payment: %v", err)
|
||||
return "", err
|
||||
}
|
||||
|
||||
|
|
@ -157,7 +159,7 @@ func (gs *GreenlightService) GetBalance(ctx context.Context) (balance int64, err
|
|||
response, err := gs.client.ListFunds(glalby.ListFundsRequest{})
|
||||
|
||||
if err != nil {
|
||||
gs.svc.Logger.Errorf("Failed to list funds: %v", err)
|
||||
gs.logger.Errorf("Failed to list funds: %v", err)
|
||||
return 0, err
|
||||
}
|
||||
|
||||
|
|
@ -171,7 +173,7 @@ func (gs *GreenlightService) GetBalance(ctx context.Context) (balance int64, err
|
|||
return balance, nil
|
||||
}
|
||||
|
||||
func (gs *GreenlightService) MakeInvoice(ctx context.Context, amount int64, description string, descriptionHash string, expiry int64) (transaction *Nip47Transaction, err error) {
|
||||
func (gs *GreenlightService) MakeInvoice(ctx context.Context, amount int64, description string, descriptionHash string, expiry int64) (transaction *nip47.Transaction, err error) {
|
||||
uexpiry := uint64(expiry)
|
||||
// TODO: it seems description hash cannot be passed to greenlight
|
||||
invoice, err := gs.client.MakeInvoice(glalby.MakeInvoiceRequest{
|
||||
|
|
@ -182,13 +184,13 @@ func (gs *GreenlightService) MakeInvoice(ctx context.Context, amount int64, desc
|
|||
})
|
||||
|
||||
if err != nil {
|
||||
gs.svc.Logger.Errorf("MakeInvoice failed: %v", err)
|
||||
gs.logger.Errorf("MakeInvoice failed: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
paymentRequest, err := decodepay.Decodepay(strings.ToLower(invoice.Bolt11))
|
||||
if err != nil {
|
||||
gs.svc.Logger.WithFields(logrus.Fields{
|
||||
gs.logger.WithFields(logrus.Fields{
|
||||
"invoice": invoice.Bolt11,
|
||||
}).Errorf("Failed to decode bolt11 invoice: %v", invoice.Bolt11)
|
||||
return nil, err
|
||||
|
|
@ -197,7 +199,7 @@ func (gs *GreenlightService) MakeInvoice(ctx context.Context, amount int64, desc
|
|||
description = paymentRequest.Description
|
||||
descriptionHash = paymentRequest.DescriptionHash
|
||||
expiresAt := int64(invoice.ExpiresAt)
|
||||
transaction = &Nip47Transaction{
|
||||
transaction = &nip47.Transaction{
|
||||
Type: "incoming",
|
||||
Invoice: invoice.Bolt11,
|
||||
Description: description,
|
||||
|
|
@ -211,13 +213,13 @@ func (gs *GreenlightService) MakeInvoice(ctx context.Context, amount int64, desc
|
|||
return transaction, nil
|
||||
}
|
||||
|
||||
func (gs *GreenlightService) LookupInvoice(ctx context.Context, paymentHash string) (transaction *Nip47Transaction, err error) {
|
||||
func (gs *GreenlightService) LookupInvoice(ctx context.Context, paymentHash string) (transaction *nip47.Transaction, err error) {
|
||||
response, err := gs.client.ListInvoices(glalby.ListInvoicesRequest{
|
||||
PaymentHash: &paymentHash,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
gs.svc.Logger.Errorf("ListInvoices failed: %v", err)
|
||||
gs.logger.Errorf("ListInvoices failed: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
|
@ -233,22 +235,22 @@ func (gs *GreenlightService) LookupInvoice(ctx context.Context, paymentHash stri
|
|||
transaction, err = gs.greenlightInvoiceToTransaction(&invoice)
|
||||
|
||||
if err != nil {
|
||||
gs.svc.Logger.Errorf("Failed to map invoice: %v", err)
|
||||
gs.logger.Errorf("Failed to map invoice: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return transaction, nil
|
||||
}
|
||||
|
||||
func (gs *GreenlightService) ListTransactions(ctx context.Context, from, until, limit, offset uint64, unpaid bool, invoiceType string) (transactions []Nip47Transaction, err error) {
|
||||
func (gs *GreenlightService) ListTransactions(ctx context.Context, from, until, limit, offset uint64, unpaid bool, invoiceType string) (transactions []nip47.Transaction, err error) {
|
||||
listInvoicesResponse, err := gs.client.ListInvoices(glalby.ListInvoicesRequest{})
|
||||
|
||||
if err != nil {
|
||||
gs.svc.Logger.Errorf("ListInvoices failed: %v", err)
|
||||
gs.logger.Errorf("ListInvoices failed: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
transactions = []Nip47Transaction{}
|
||||
transactions = []nip47.Transaction{}
|
||||
|
||||
if err != nil {
|
||||
log.Printf("ListInvoices failed: %v", err)
|
||||
|
|
@ -272,7 +274,7 @@ func (gs *GreenlightService) ListTransactions(ctx context.Context, from, until,
|
|||
listPaymentsResponse, err := gs.client.ListPayments(glalby.ListPaymentsRequest{})
|
||||
|
||||
if err != nil {
|
||||
gs.svc.Logger.Errorf("ListPayments failed: %v", err)
|
||||
gs.logger.Errorf("ListPayments failed: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
|
@ -341,7 +343,7 @@ func (gs *GreenlightService) GetInfo(ctx context.Context) (info *lnclient.NodeIn
|
|||
nodeInfo, err := gs.client.GetInfo()
|
||||
|
||||
if err != nil {
|
||||
gs.svc.Logger.Errorf("GetInfo failed: %v", err)
|
||||
gs.logger.Errorf("GetInfo failed: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
|
@ -359,7 +361,7 @@ func (gs *GreenlightService) ListChannels(ctx context.Context) ([]lnclient.Chann
|
|||
response, err := gs.client.ListFunds(glalby.ListFundsRequest{})
|
||||
|
||||
if err != nil {
|
||||
gs.svc.Logger.Errorf("Failed to list funds: %v", err)
|
||||
gs.logger.Errorf("Failed to list funds: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
|
@ -396,7 +398,7 @@ func (gs *GreenlightService) ListChannels(ctx context.Context) ([]lnclient.Chann
|
|||
func (gs *GreenlightService) GetNodeConnectionInfo(ctx context.Context) (nodeConnectionInfo *lnclient.NodeConnectionInfo, err error) {
|
||||
info, err := gs.GetInfo(ctx)
|
||||
if err != nil {
|
||||
gs.svc.Logger.Errorf("GetInfo failed: %v", err)
|
||||
gs.logger.Errorf("GetInfo failed: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
return &lnclient.NodeConnectionInfo{
|
||||
|
|
@ -419,7 +421,7 @@ func (gs *GreenlightService) ConnectPeer(ctx context.Context, connectPeerRequest
|
|||
Port: port,
|
||||
})
|
||||
if err != nil {
|
||||
gs.svc.Logger.Errorf("ConnectPeer failed: %v", err)
|
||||
gs.logger.Errorf("ConnectPeer failed: %v", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
|
@ -436,7 +438,7 @@ func (gs *GreenlightService) OpenChannel(ctx context.Context, openChannelRequest
|
|||
// Minconf: &minConf,
|
||||
})
|
||||
if err != nil {
|
||||
gs.svc.Logger.Errorf("OpenChannel failed: %v", err)
|
||||
gs.logger.Errorf("OpenChannel failed: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
|
@ -450,7 +452,7 @@ func (gs *GreenlightService) CloseChannel(ctx context.Context, closeChannelReque
|
|||
Id: closeChannelRequest.ChannelId,
|
||||
})
|
||||
if err != nil {
|
||||
gs.svc.Logger.WithError(err).Error("CloseChannel failed")
|
||||
gs.logger.WithError(err).Error("CloseChannel failed")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
|
@ -461,7 +463,7 @@ func (gs *GreenlightService) GetNewOnchainAddress(ctx context.Context) (string,
|
|||
|
||||
newAddressResponse, err := gs.client.NewAddress(glalby.NewAddressRequest{})
|
||||
if err != nil {
|
||||
gs.svc.Logger.Errorf("NewAddress failed: %v", err)
|
||||
gs.logger.Errorf("NewAddress failed: %v", err)
|
||||
return "", err
|
||||
}
|
||||
if newAddressResponse.Bech32 == nil {
|
||||
|
|
@ -473,10 +475,10 @@ func (gs *GreenlightService) GetNewOnchainAddress(ctx context.Context) (string,
|
|||
|
||||
func (gs *GreenlightService) GetOnchainBalance(ctx context.Context) (*lnclient.OnchainBalanceResponse, error) {
|
||||
response, err := gs.client.ListFunds(glalby.ListFundsRequest{})
|
||||
gs.svc.Logger.WithField("response", response).Info("Listed funds")
|
||||
gs.logger.WithField("response", response).Info("Listed funds")
|
||||
|
||||
if err != nil {
|
||||
gs.svc.Logger.Errorf("Failed to list funds: %v", err)
|
||||
gs.logger.Errorf("Failed to list funds: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
|
@ -505,10 +507,10 @@ func (gs *GreenlightService) RedeemOnchainFunds(ctx context.Context, toAddress s
|
|||
Amount: &amountAll,
|
||||
})
|
||||
if err != nil {
|
||||
gs.svc.Logger.WithError(err).Error("Withdraw failed")
|
||||
gs.logger.WithError(err).Error("Withdraw failed")
|
||||
return "", err
|
||||
}
|
||||
gs.svc.Logger.WithField("txId", txId).Info("Redeeming On-Chain funds")
|
||||
gs.logger.WithField("txId", txId).Info("Redeeming On-Chain funds")
|
||||
|
||||
return txId.Txid, nil
|
||||
}
|
||||
|
|
@ -535,14 +537,14 @@ func (gs *GreenlightService) SignMessage(ctx context.Context, message string) (s
|
|||
})
|
||||
|
||||
if err != nil {
|
||||
gs.svc.Logger.Errorf("SignMessage failed: %v", err)
|
||||
gs.logger.Errorf("SignMessage failed: %v", err)
|
||||
return "", err
|
||||
}
|
||||
|
||||
return response.Zbase, nil
|
||||
}
|
||||
|
||||
func (gs *GreenlightService) greenlightInvoiceToTransaction(invoice *glalby.ListInvoicesInvoice) (*Nip47Transaction, error) {
|
||||
func (gs *GreenlightService) greenlightInvoiceToTransaction(invoice *glalby.ListInvoicesInvoice) (*nip47.Transaction, error) {
|
||||
description := ""
|
||||
descriptionHash := ""
|
||||
if invoice.Description != nil {
|
||||
|
|
@ -551,7 +553,7 @@ func (gs *GreenlightService) greenlightInvoiceToTransaction(invoice *glalby.List
|
|||
bolt11 := *invoice.Bolt11
|
||||
paymentRequest, err := decodepay.Decodepay(strings.ToLower(bolt11))
|
||||
if err != nil {
|
||||
gs.svc.Logger.WithFields(logrus.Fields{
|
||||
gs.logger.WithFields(logrus.Fields{
|
||||
"invoice": bolt11,
|
||||
}).Errorf("Failed to decode bolt11 invoice: %v", bolt11)
|
||||
return nil, err
|
||||
|
|
@ -580,7 +582,7 @@ func (gs *GreenlightService) greenlightInvoiceToTransaction(invoice *glalby.List
|
|||
settledAt = &paidAt
|
||||
}
|
||||
|
||||
transaction := &Nip47Transaction{
|
||||
transaction := &nip47.Transaction{
|
||||
Type: "incoming",
|
||||
Invoice: bolt11,
|
||||
Description: description,
|
||||
|
|
@ -603,14 +605,14 @@ func (gs *GreenlightService) ResetRouter(key string) error {
|
|||
func (gs *GreenlightService) GetBalances(ctx context.Context) (*lnclient.BalancesResponse, error) {
|
||||
onchainBalance, err := gs.GetOnchainBalance(ctx)
|
||||
if err != nil {
|
||||
gs.svc.Logger.WithError(err).Error("Failed to retrieve onchain balance")
|
||||
gs.logger.WithError(err).Error("Failed to retrieve onchain balance")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
response, err := gs.client.ListFunds(glalby.ListFundsRequest{})
|
||||
|
||||
if err != nil {
|
||||
gs.svc.Logger.Errorf("Failed to list funds: %v", err)
|
||||
gs.logger.Errorf("Failed to list funds: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
|
@ -662,3 +664,5 @@ func (gs *GreenlightService) GetNodeStatus(ctx context.Context) (nodeStatus *lnc
|
|||
func (gs *GreenlightService) GetNetworkGraph(nodeIds []string) (lnclient.NetworkGraphResponse, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (gs *GreenlightService) UpdateLastWalletSyncRequest() {}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
// TODO: move to greenlight/models.go
|
||||
package greenlight
|
||||
|
||||
import "github.com/getAlby/nostr-wallet-connect/models/lnclient"
|
||||
import "github.com/getAlby/nostr-wallet-connect/lnclient"
|
||||
|
||||
type NodeInfo struct {
|
||||
ID string `json:"id"`
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package ldk
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
|
@ -20,27 +20,31 @@ import (
|
|||
decodepay "github.com/nbd-wtf/ln-decodepay"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/getAlby/nostr-wallet-connect/config"
|
||||
"github.com/getAlby/nostr-wallet-connect/events"
|
||||
"github.com/getAlby/nostr-wallet-connect/models/config"
|
||||
"github.com/getAlby/nostr-wallet-connect/models/lnclient"
|
||||
"github.com/getAlby/nostr-wallet-connect/models/lsp"
|
||||
"github.com/getAlby/nostr-wallet-connect/lnclient"
|
||||
"github.com/getAlby/nostr-wallet-connect/lsp"
|
||||
"github.com/getAlby/nostr-wallet-connect/nip47"
|
||||
"github.com/getAlby/nostr-wallet-connect/utils"
|
||||
)
|
||||
|
||||
type LDKService struct {
|
||||
svc *Service
|
||||
workdir string
|
||||
node *ldk_node.Node
|
||||
ldkEventBroadcaster LDKEventBroadcaster
|
||||
cancel context.CancelFunc
|
||||
network string
|
||||
eventPublisher events.EventPublisher
|
||||
syncing bool
|
||||
lastSync time.Time
|
||||
workdir string
|
||||
node *ldk_node.Node
|
||||
ldkEventBroadcaster LDKEventBroadcaster
|
||||
cancel context.CancelFunc
|
||||
network string
|
||||
eventPublisher events.EventPublisher
|
||||
syncing bool
|
||||
lastSync time.Time
|
||||
logger *logrus.Logger
|
||||
cfg config.Config
|
||||
lastWalletSyncRequest time.Time
|
||||
}
|
||||
|
||||
const resetRouterKey = "ResetRouter"
|
||||
|
||||
func NewLDKService(ctx context.Context, svc *Service, mnemonic, workDir string, network string, esploraServer string, gossipSource string) (result lnclient.LNClient, err error) {
|
||||
func NewLDKService(ctx context.Context, logger *logrus.Logger, cfg config.Config, eventPublisher events.EventPublisher, mnemonic, workDir string, network string, esploraServer string, gossipSource string) (result lnclient.LNClient, err error) {
|
||||
if mnemonic == "" || workDir == "" {
|
||||
return nil, errors.New("one or more required LDK configuration are missing")
|
||||
}
|
||||
|
|
@ -79,7 +83,7 @@ func NewLDKService(ctx context.Context, svc *Service, mnemonic, workDir string,
|
|||
|
||||
config.ListeningAddresses = &listeningAddresses
|
||||
config.LogDirPath = &logDirPath
|
||||
logLevel, err := strconv.Atoi(svc.cfg.Env.LDKLogLevel)
|
||||
logLevel, err := strconv.Atoi(cfg.GetEnv().LDKLogLevel)
|
||||
if err == nil {
|
||||
config.LogLevel = ldk_node.LogLevel(logLevel)
|
||||
}
|
||||
|
|
@ -88,10 +92,10 @@ func NewLDKService(ctx context.Context, svc *Service, mnemonic, workDir string,
|
|||
builder.SetNetwork(network)
|
||||
builder.SetEsploraServer(esploraServer)
|
||||
if gossipSource != "" {
|
||||
svc.Logger.WithField("gossipSource", gossipSource).Warn("LDK RGS instance set")
|
||||
logger.WithField("gossipSource", gossipSource).Warn("LDK RGS instance set")
|
||||
builder.SetGossipSourceRgs(gossipSource)
|
||||
} else {
|
||||
svc.Logger.Warn("No LDK RGS instance set")
|
||||
logger.Warn("No LDK RGS instance set")
|
||||
}
|
||||
builder.SetStorageDirPath(filepath.Join(newpath, "./storage"))
|
||||
|
||||
|
|
@ -103,33 +107,34 @@ func NewLDKService(ctx context.Context, svc *Service, mnemonic, workDir string,
|
|||
node, err := builder.Build()
|
||||
|
||||
if err != nil {
|
||||
svc.Logger.Errorf("Failed to create LDK node: %v", err)
|
||||
logger.Errorf("Failed to create LDK node: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ldkEventConsumer := make(chan *ldk_node.Event)
|
||||
ldkCtx, cancel := context.WithCancel(ctx)
|
||||
ldkEventBroadcaster := NewLDKEventBroadcaster(svc.Logger, ldkCtx, ldkEventConsumer)
|
||||
ldkEventBroadcaster := NewLDKEventBroadcaster(logger, ldkCtx, ldkEventConsumer)
|
||||
|
||||
ls := LDKService{
|
||||
workdir: newpath,
|
||||
node: node,
|
||||
svc: svc,
|
||||
cancel: cancel,
|
||||
ldkEventBroadcaster: ldkEventBroadcaster,
|
||||
network: network,
|
||||
eventPublisher: svc.EventPublisher,
|
||||
eventPublisher: eventPublisher,
|
||||
logger: logger,
|
||||
cfg: cfg,
|
||||
}
|
||||
|
||||
// TODO: remove when LDK supports this
|
||||
deleteOldLDKLogs(svc.Logger, logDirPath)
|
||||
deleteOldLDKLogs(logger, logDirPath)
|
||||
go func() {
|
||||
// delete old LDK logs every 24 hours
|
||||
ticker := time.NewTicker(24 * time.Hour)
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
deleteOldLDKLogs(svc.Logger, logDirPath)
|
||||
deleteOldLDKLogs(logger, logDirPath)
|
||||
case <-ldkCtx.Done():
|
||||
return
|
||||
}
|
||||
|
|
@ -152,7 +157,7 @@ func NewLDKService(ctx context.Context, svc *Service, mnemonic, workDir string,
|
|||
continue
|
||||
}
|
||||
|
||||
ls.logLdkEvent(ldkCtx, event)
|
||||
ls.handleLdkEvent(ldkCtx, event)
|
||||
ldkEventConsumer <- event
|
||||
|
||||
node.EventHandled()
|
||||
|
|
@ -162,12 +167,12 @@ func NewLDKService(ctx context.Context, svc *Service, mnemonic, workDir string,
|
|||
|
||||
err = node.Start()
|
||||
if err != nil {
|
||||
svc.Logger.Errorf("Failed to start LDK node: %v", err)
|
||||
logger.Errorf("Failed to start LDK node: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nodeId := node.NodeId()
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
logger.WithFields(logrus.Fields{
|
||||
"nodeId": nodeId,
|
||||
"status": node.Status(),
|
||||
}).Info("Started LDK node. Syncing wallet...")
|
||||
|
|
@ -175,17 +180,17 @@ func NewLDKService(ctx context.Context, svc *Service, mnemonic, workDir string,
|
|||
syncStartTime := time.Now()
|
||||
err = node.SyncWallets()
|
||||
if err != nil {
|
||||
svc.Logger.WithError(err).Error("Failed to sync LDK wallets")
|
||||
logger.WithError(err).Error("Failed to sync LDK wallets")
|
||||
shutdownErr := ls.Shutdown()
|
||||
if shutdownErr != nil {
|
||||
svc.Logger.WithError(shutdownErr).Error("Failed to shutdown LDK node")
|
||||
logger.WithError(shutdownErr).Error("Failed to shutdown LDK node")
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
ls.lastSync = time.Now()
|
||||
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
logger.WithFields(logrus.Fields{
|
||||
"nodeId": nodeId,
|
||||
"status": node.Status(),
|
||||
"duration": math.Ceil(time.Since(syncStartTime).Seconds()),
|
||||
|
|
@ -193,18 +198,19 @@ func NewLDKService(ctx context.Context, svc *Service, mnemonic, workDir string,
|
|||
|
||||
if ls.network == "bitcoin" {
|
||||
// try to connect to some peers to retrieve P2P gossip data. TODO: Remove once LDK can correctly do gossip with CLN and Eclair nodes
|
||||
// see https://github.com/lightningdevkit/rust-lightning/issues/3075
|
||||
peers := []string{
|
||||
"031b301307574bbe9b9ac7b79cbe1700e31e544513eae0b5d7497483083f99e581@45.79.192.236:9735", // Olympus
|
||||
"0364913d18a19c671bb36dd04d6ad5be0fe8f2894314c36a9db3f03c2d414907e1@192.243.215.102:9735", // LQwD
|
||||
"035e4ff418fc8b5554c5d9eea66396c227bd429a3251c8cbc711002ba215bfc226@170.75.163.209:9735", // WoS
|
||||
"02fcc5bfc48e83f06c04483a2985e1c390cb0f35058baa875ad2053858b8e80dbd@35.239.148.251:9735", // Blink
|
||||
}
|
||||
svc.Logger.Info("Connecting to some peers to retrieve P2P gossip data")
|
||||
logger.Info("Connecting to some peers to retrieve P2P gossip data")
|
||||
for _, peer := range peers {
|
||||
parts := strings.FieldsFunc(peer, func(r rune) bool { return r == '@' || r == ':' })
|
||||
port, err := strconv.ParseUint(parts[2], 10, 16)
|
||||
if err != nil {
|
||||
svc.Logger.WithError(err).Error("Failed to parse port number")
|
||||
logger.WithError(err).Error("Failed to parse port number")
|
||||
continue
|
||||
}
|
||||
err = ls.ConnectPeer(ctx, &lnclient.ConnectPeerRequest{
|
||||
|
|
@ -213,7 +219,7 @@ func NewLDKService(ctx context.Context, svc *Service, mnemonic, workDir string,
|
|||
Port: uint16(port),
|
||||
})
|
||||
if err != nil {
|
||||
svc.Logger.WithField("peer", peer).WithError(err).Error("Failed to connect to peer")
|
||||
logger.WithField("peer", peer).WithError(err).Error("Failed to connect to peer")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -228,26 +234,26 @@ func NewLDKService(ctx context.Context, svc *Service, mnemonic, workDir string,
|
|||
return
|
||||
case <-time.After(MIN_SYNC_INTERVAL):
|
||||
|
||||
if time.Since(ls.svc.lastWalletSyncRequest) > MIN_SYNC_INTERVAL && time.Since(ls.lastSync) < MAX_SYNC_INTERVAL {
|
||||
// ls.svc.Logger.Debug("skipping background wallet sync")
|
||||
if time.Since(ls.lastWalletSyncRequest) > MIN_SYNC_INTERVAL && time.Since(ls.lastSync) < MAX_SYNC_INTERVAL {
|
||||
// ls.logger.Debug("skipping background wallet sync")
|
||||
continue
|
||||
}
|
||||
|
||||
ls.svc.Logger.Info("Starting background wallet sync")
|
||||
ls.logger.Info("Starting background wallet sync")
|
||||
syncStartTime := time.Now()
|
||||
ls.syncing = true
|
||||
err = node.SyncWallets()
|
||||
ls.syncing = false
|
||||
|
||||
if err != nil {
|
||||
svc.Logger.WithError(err).Error("Failed to sync LDK wallets")
|
||||
logger.WithError(err).Error("Failed to sync LDK wallets")
|
||||
// try again at next MIN_SYNC_INTERVAL
|
||||
continue
|
||||
}
|
||||
|
||||
ls.lastSync = time.Now()
|
||||
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
logger.WithFields(logrus.Fields{
|
||||
"nodeId": nodeId,
|
||||
"status": node.Status(),
|
||||
"duration": math.Ceil(time.Since(syncStartTime).Seconds()),
|
||||
|
|
@ -261,23 +267,23 @@ func NewLDKService(ctx context.Context, svc *Service, mnemonic, workDir string,
|
|||
|
||||
func (ls *LDKService) Shutdown() error {
|
||||
if ls.node == nil {
|
||||
ls.svc.Logger.Infof("LDK client already shut down")
|
||||
ls.logger.Infof("LDK client already shut down")
|
||||
return nil
|
||||
}
|
||||
// make sure nothing else can use it
|
||||
node := ls.node
|
||||
ls.node = nil
|
||||
|
||||
ls.svc.Logger.Infof("shutting down LDK client")
|
||||
ls.svc.Logger.Infof("cancelling LDK context")
|
||||
ls.logger.Infof("shutting down LDK client")
|
||||
ls.logger.Infof("cancelling LDK context")
|
||||
ls.cancel()
|
||||
|
||||
for ls.syncing {
|
||||
ls.svc.Logger.Infof("Waiting for background sync to finish before stopping LDK node...")
|
||||
ls.logger.Infof("Waiting for background sync to finish before stopping LDK node...")
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
|
||||
ls.svc.Logger.Infof("stopping LDK node")
|
||||
ls.logger.Infof("stopping LDK node")
|
||||
shutdownChannel := make(chan error)
|
||||
go func() {
|
||||
shutdownChannel <- node.Stop()
|
||||
|
|
@ -286,44 +292,44 @@ func (ls *LDKService) Shutdown() error {
|
|||
select {
|
||||
case err := <-shutdownChannel:
|
||||
if err != nil {
|
||||
ls.svc.Logger.WithError(err).Error("Failed to stop LDK node")
|
||||
ls.logger.WithError(err).Error("Failed to stop LDK node")
|
||||
// do not return error - we still need to destroy the node
|
||||
} else {
|
||||
ls.svc.Logger.Info("LDK stop node succeeded")
|
||||
ls.logger.Info("LDK stop node succeeded")
|
||||
}
|
||||
case <-time.After(120 * time.Second):
|
||||
ls.svc.Logger.Error("Timeout shutting down LDK node after 120 seconds")
|
||||
ls.logger.Error("Timeout shutting down LDK node after 120 seconds")
|
||||
}
|
||||
|
||||
ls.svc.Logger.Infof("Destroying node object")
|
||||
ls.logger.Infof("Destroying node object")
|
||||
node.Destroy()
|
||||
|
||||
ls.resetRouterInternal()
|
||||
|
||||
ls.svc.Logger.Infof("LDK shutdown complete")
|
||||
ls.logger.Infof("LDK shutdown complete")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ls *LDKService) resetRouterInternal() {
|
||||
key, err := ls.svc.cfg.Get(resetRouterKey, "")
|
||||
key, err := ls.cfg.Get(resetRouterKey, "")
|
||||
|
||||
if err != nil {
|
||||
ls.svc.Logger.Error("Failed to retrieve ResetRouter key")
|
||||
ls.logger.Error("Failed to retrieve ResetRouter key")
|
||||
}
|
||||
|
||||
if key != "" {
|
||||
ls.svc.cfg.SetUpdate(resetRouterKey, "", "")
|
||||
ls.svc.Logger.WithField("key", key).Info("Resetting router")
|
||||
ls.cfg.SetUpdate(resetRouterKey, "", "")
|
||||
ls.logger.WithField("key", key).Info("Resetting router")
|
||||
|
||||
ldkDbPath := filepath.Join(ls.workdir, "storage", "ldk_node_data.sqlite")
|
||||
if _, err := os.Stat(ldkDbPath); errors.Is(err, os.ErrNotExist) {
|
||||
ls.svc.Logger.Error("Could not find LDK database")
|
||||
ls.logger.Error("Could not find LDK database")
|
||||
return
|
||||
}
|
||||
ldkDb, err := sql.Open("sqlite", ldkDbPath)
|
||||
if err != nil {
|
||||
ls.svc.Logger.Error("Could not open LDK DB file")
|
||||
ls.logger.Error("Could not open LDK DB file")
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -339,43 +345,43 @@ func (ls *LDKService) resetRouterInternal() {
|
|||
case "NetworkGraph":
|
||||
command = "delete from ldk_node_data where key = 'network_graph';VACUUM;"
|
||||
default:
|
||||
ls.svc.Logger.WithField("key", key).Error("Unknown reset router key")
|
||||
ls.logger.WithField("key", key).Error("Unknown reset router key")
|
||||
return
|
||||
}
|
||||
|
||||
result, err := ldkDb.Exec(command)
|
||||
if err != nil {
|
||||
ls.svc.Logger.WithError(err).Error("Failed execute reset command")
|
||||
ls.logger.WithError(err).Error("Failed execute reset command")
|
||||
return
|
||||
}
|
||||
rowsAffected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
ls.svc.Logger.WithError(err).Error("Failed to get rows affected")
|
||||
ls.logger.WithError(err).Error("Failed to get rows affected")
|
||||
return
|
||||
}
|
||||
ls.svc.Logger.WithFields(logrus.Fields{
|
||||
ls.logger.WithFields(logrus.Fields{
|
||||
"rowsAffected": rowsAffected,
|
||||
}).Info("Reset router")
|
||||
|
||||
if err != nil {
|
||||
ls.svc.Logger.WithField("key", key).WithError(err).Error("ResetRouter failed")
|
||||
ls.logger.WithField("key", key).WithError(err).Error("ResetRouter failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (gs *LDKService) SendPaymentSync(ctx context.Context, invoice string) (*lnclient.Nip47PayInvoiceResponse, error) {
|
||||
func (ls *LDKService) SendPaymentSync(ctx context.Context, invoice string) (*lnclient.PayInvoiceResponse, error) {
|
||||
paymentRequest, err := decodepay.Decodepay(invoice)
|
||||
if err != nil {
|
||||
gs.svc.Logger.WithFields(logrus.Fields{
|
||||
ls.logger.WithFields(logrus.Fields{
|
||||
"bolt11": invoice,
|
||||
}).Errorf("Failed to decode bolt11 invoice: %v", err)
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
maxSpendable := gs.getMaxSpendable()
|
||||
maxSpendable := ls.getMaxSpendable()
|
||||
if paymentRequest.MSatoshi > maxSpendable {
|
||||
gs.eventPublisher.Publish(&events.Event{
|
||||
ls.eventPublisher.Publish(&events.Event{
|
||||
Event: "nwc_outgoing_liquidity_required",
|
||||
Properties: map[string]interface{}{
|
||||
//"amount": amount / 1000,
|
||||
|
|
@ -387,12 +393,12 @@ func (gs *LDKService) SendPaymentSync(ctx context.Context, invoice string) (*lnc
|
|||
}
|
||||
|
||||
paymentStart := time.Now()
|
||||
ldkEventSubscription := gs.ldkEventBroadcaster.Subscribe()
|
||||
defer gs.ldkEventBroadcaster.CancelSubscription(ldkEventSubscription)
|
||||
ldkEventSubscription := ls.ldkEventBroadcaster.Subscribe()
|
||||
defer ls.ldkEventBroadcaster.CancelSubscription(ldkEventSubscription)
|
||||
|
||||
paymentHash, err := gs.node.Bolt11Payment().Send(invoice)
|
||||
paymentHash, err := ls.node.Bolt11Payment().Send(invoice)
|
||||
if err != nil {
|
||||
gs.svc.Logger.WithError(err).Error("SendPayment failed")
|
||||
ls.logger.WithError(err).Error("SendPayment failed")
|
||||
return nil, err
|
||||
}
|
||||
fee := uint64(0)
|
||||
|
|
@ -405,23 +411,23 @@ func (gs *LDKService) SendPaymentSync(ctx context.Context, invoice string) (*lnc
|
|||
eventPaymentFailed, isEventPaymentFailedEvent := (*event).(ldk_node.EventPaymentFailed)
|
||||
|
||||
if isEventPaymentSuccessfulEvent && eventPaymentSuccessful.PaymentHash == paymentHash {
|
||||
gs.svc.Logger.Info("Got payment success event")
|
||||
payment := gs.node.Payment(paymentHash)
|
||||
ls.logger.Info("Got payment success event")
|
||||
payment := ls.node.Payment(paymentHash)
|
||||
if payment == nil {
|
||||
gs.svc.Logger.Errorf("Couldn't find payment by payment hash: %v", paymentHash)
|
||||
ls.logger.Errorf("Couldn't find payment by payment hash: %v", paymentHash)
|
||||
return nil, errors.New("Payment not found")
|
||||
}
|
||||
|
||||
bolt11PaymentKind, ok := payment.Kind.(ldk_node.PaymentKindBolt11)
|
||||
|
||||
if !ok {
|
||||
gs.svc.Logger.WithFields(logrus.Fields{
|
||||
ls.logger.WithFields(logrus.Fields{
|
||||
"payment": payment,
|
||||
}).Error("Payment is not a bolt11 kind")
|
||||
}
|
||||
|
||||
if bolt11PaymentKind.Preimage == nil {
|
||||
gs.svc.Logger.Errorf("No payment preimage for payment hash: %v", paymentHash)
|
||||
ls.logger.Errorf("No payment preimage for payment hash: %v", paymentHash)
|
||||
return nil, errors.New("Payment preimage not found")
|
||||
}
|
||||
preimage = *bolt11PaymentKind.Preimage
|
||||
|
|
@ -454,7 +460,7 @@ func (gs *LDKService) SendPaymentSync(ctx context.Context, invoice string) (*lnc
|
|||
failureReasonMessage = "UnknownError"
|
||||
}
|
||||
|
||||
gs.svc.Logger.WithFields(logrus.Fields{
|
||||
ls.logger.WithFields(logrus.Fields{
|
||||
"paymentHash": paymentHash,
|
||||
"failureReason": failureReason,
|
||||
"failureReasonMessage": failureReasonMessage,
|
||||
|
|
@ -468,18 +474,18 @@ func (gs *LDKService) SendPaymentSync(ctx context.Context, invoice string) (*lnc
|
|||
return nil, errors.New("Payment timed out")
|
||||
}
|
||||
|
||||
gs.svc.Logger.WithFields(logrus.Fields{
|
||||
ls.logger.WithFields(logrus.Fields{
|
||||
"duration": time.Since(paymentStart).Milliseconds(),
|
||||
"fee": fee,
|
||||
}).Info("Successful payment")
|
||||
|
||||
return &lnclient.Nip47PayInvoiceResponse{
|
||||
return &lnclient.PayInvoiceResponse{
|
||||
Preimage: preimage,
|
||||
Fee: &fee,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (gs *LDKService) SendKeysend(ctx context.Context, amount int64, destination, preimage string, custom_records []lnclient.TLVRecord) (preImage string, err error) {
|
||||
func (ls *LDKService) SendKeysend(ctx context.Context, amount int64, destination, preimage string, custom_records []lnclient.TLVRecord) (preImage string, err error) {
|
||||
paymentStart := time.Now()
|
||||
customTlvs := []ldk_node.TlvEntry{}
|
||||
|
||||
|
|
@ -490,16 +496,16 @@ func (gs *LDKService) SendKeysend(ctx context.Context, amount int64, destination
|
|||
})
|
||||
}
|
||||
|
||||
ldkEventSubscription := gs.ldkEventBroadcaster.Subscribe()
|
||||
defer gs.ldkEventBroadcaster.CancelSubscription(ldkEventSubscription)
|
||||
ldkEventSubscription := ls.ldkEventBroadcaster.Subscribe()
|
||||
defer ls.ldkEventBroadcaster.CancelSubscription(ldkEventSubscription)
|
||||
|
||||
paymentHash, err := gs.node.SpontaneousPayment().Send(uint64(amount), destination, customTlvs)
|
||||
paymentHash, err := ls.node.SpontaneousPayment().Send(uint64(amount), destination, customTlvs)
|
||||
if err != nil {
|
||||
gs.svc.Logger.WithError(err).Error("Keysend failed")
|
||||
ls.logger.WithError(err).Error("Keysend failed")
|
||||
return "", err
|
||||
}
|
||||
|
||||
gs.svc.Logger.Infof("TODO: listen for events %v", paymentHash)
|
||||
ls.logger.Infof("TODO: listen for events %v", paymentHash)
|
||||
|
||||
fee := uint64(0)
|
||||
|
||||
|
|
@ -510,23 +516,23 @@ func (gs *LDKService) SendKeysend(ctx context.Context, amount int64, destination
|
|||
eventPaymentFailed, isEventPaymentFailedEvent := (*event).(ldk_node.EventPaymentFailed)
|
||||
|
||||
if isEventPaymentSuccessfulEvent && eventPaymentSuccessful.PaymentHash == paymentHash {
|
||||
gs.svc.Logger.Info("Got payment success event")
|
||||
payment := gs.node.Payment(paymentHash)
|
||||
ls.logger.Info("Got payment success event")
|
||||
payment := ls.node.Payment(paymentHash)
|
||||
if payment == nil {
|
||||
gs.svc.Logger.Errorf("Couldn't find payment by payment hash: %v", paymentHash)
|
||||
ls.logger.Errorf("Couldn't find payment by payment hash: %v", paymentHash)
|
||||
return "", errors.New("Payment not found")
|
||||
}
|
||||
|
||||
spontaneousPaymentKind, ok := payment.Kind.(ldk_node.PaymentKindSpontaneous)
|
||||
|
||||
if !ok {
|
||||
gs.svc.Logger.WithFields(logrus.Fields{
|
||||
ls.logger.WithFields(logrus.Fields{
|
||||
"payment": payment,
|
||||
}).Error("Payment is not a spontaneous kind")
|
||||
}
|
||||
|
||||
if spontaneousPaymentKind.Preimage == nil {
|
||||
gs.svc.Logger.Errorf("No payment preimage for payment hash: %v", paymentHash)
|
||||
ls.logger.Errorf("No payment preimage for payment hash: %v", paymentHash)
|
||||
return "", errors.New("Payment preimage not found")
|
||||
}
|
||||
preimage = *spontaneousPaymentKind.Preimage
|
||||
|
|
@ -559,7 +565,7 @@ func (gs *LDKService) SendKeysend(ctx context.Context, amount int64, destination
|
|||
failureReasonMessage = "UnknownError"
|
||||
}
|
||||
|
||||
gs.svc.Logger.WithFields(logrus.Fields{
|
||||
ls.logger.WithFields(logrus.Fields{
|
||||
"paymentHash": paymentHash,
|
||||
"failureReason": failureReason,
|
||||
"failureReasonMessage": failureReasonMessage,
|
||||
|
|
@ -573,15 +579,15 @@ func (gs *LDKService) SendKeysend(ctx context.Context, amount int64, destination
|
|||
return "", errors.New("keysend payment timed out")
|
||||
}
|
||||
|
||||
gs.svc.Logger.WithFields(logrus.Fields{
|
||||
ls.logger.WithFields(logrus.Fields{
|
||||
"duration": time.Since(paymentStart).Milliseconds(),
|
||||
"fee": fee,
|
||||
}).Info("Successful keysend payment")
|
||||
return preimage, nil
|
||||
}
|
||||
|
||||
func (gs *LDKService) GetBalance(ctx context.Context) (balance int64, err error) {
|
||||
channels := gs.node.ListChannels()
|
||||
func (ls *LDKService) GetBalance(ctx context.Context) (balance int64, err error) {
|
||||
channels := ls.node.ListChannels()
|
||||
|
||||
balance = 0
|
||||
for _, channel := range channels {
|
||||
|
|
@ -593,9 +599,9 @@ func (gs *LDKService) GetBalance(ctx context.Context) (balance int64, err error)
|
|||
return balance, nil
|
||||
}
|
||||
|
||||
func (gs *LDKService) getMaxReceivable() int64 {
|
||||
func (ls *LDKService) getMaxReceivable() int64 {
|
||||
var receivable int64 = 0
|
||||
channels := gs.node.ListChannels()
|
||||
channels := ls.node.ListChannels()
|
||||
for _, channel := range channels {
|
||||
if channel.IsUsable {
|
||||
receivable += min(int64(channel.InboundCapacityMsat), int64(*channel.InboundHtlcMaximumMsat))
|
||||
|
|
@ -604,9 +610,9 @@ func (gs *LDKService) getMaxReceivable() int64 {
|
|||
return int64(receivable)
|
||||
}
|
||||
|
||||
func (gs *LDKService) getMaxSpendable() int64 {
|
||||
func (ls *LDKService) getMaxSpendable() int64 {
|
||||
var spendable int64 = 0
|
||||
channels := gs.node.ListChannels()
|
||||
channels := ls.node.ListChannels()
|
||||
for _, channel := range channels {
|
||||
if channel.IsUsable {
|
||||
spendable += min(int64(channel.OutboundCapacityMsat), int64(*channel.CounterpartyOutboundHtlcMaximumMsat))
|
||||
|
|
@ -615,12 +621,12 @@ func (gs *LDKService) getMaxSpendable() int64 {
|
|||
return int64(spendable)
|
||||
}
|
||||
|
||||
func (gs *LDKService) MakeInvoice(ctx context.Context, amount int64, description string, descriptionHash string, expiry int64) (transaction *Nip47Transaction, err error) {
|
||||
func (ls *LDKService) MakeInvoice(ctx context.Context, amount int64, description string, descriptionHash string, expiry int64) (transaction *nip47.Transaction, err error) {
|
||||
|
||||
maxReceivable := gs.getMaxReceivable()
|
||||
maxReceivable := ls.getMaxReceivable()
|
||||
|
||||
if amount > maxReceivable {
|
||||
gs.eventPublisher.Publish(&events.Event{
|
||||
ls.eventPublisher.Publish(&events.Event{
|
||||
Event: "nwc_incoming_liquidity_required",
|
||||
Properties: map[string]interface{}{
|
||||
//"amount": amount / 1000,
|
||||
|
|
@ -632,19 +638,19 @@ func (gs *LDKService) MakeInvoice(ctx context.Context, amount int64, description
|
|||
}
|
||||
|
||||
// TODO: support passing description hash
|
||||
invoice, err := gs.node.Bolt11Payment().Receive(uint64(amount),
|
||||
invoice, err := ls.node.Bolt11Payment().Receive(uint64(amount),
|
||||
description,
|
||||
uint32(expiry))
|
||||
|
||||
if err != nil {
|
||||
gs.svc.Logger.WithError(err).Error("MakeInvoice failed")
|
||||
ls.logger.WithError(err).Error("MakeInvoice failed")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var expiresAt *int64
|
||||
paymentRequest, err := decodepay.Decodepay(invoice)
|
||||
if err != nil {
|
||||
gs.svc.Logger.WithFields(logrus.Fields{
|
||||
ls.logger.WithFields(logrus.Fields{
|
||||
"bolt11": invoice,
|
||||
}).Errorf("Failed to decode bolt11 invoice: %v", err)
|
||||
|
||||
|
|
@ -655,7 +661,7 @@ func (gs *LDKService) MakeInvoice(ctx context.Context, amount int64, description
|
|||
description = paymentRequest.Description
|
||||
descriptionHash = paymentRequest.DescriptionHash
|
||||
|
||||
transaction = &Nip47Transaction{
|
||||
transaction = &nip47.Transaction{
|
||||
Type: "incoming",
|
||||
Invoice: invoice,
|
||||
PaymentHash: paymentRequest.PaymentHash,
|
||||
|
|
@ -669,26 +675,26 @@ func (gs *LDKService) MakeInvoice(ctx context.Context, amount int64, description
|
|||
return transaction, nil
|
||||
}
|
||||
|
||||
func (gs *LDKService) LookupInvoice(ctx context.Context, paymentHash string) (transaction *Nip47Transaction, err error) {
|
||||
func (ls *LDKService) LookupInvoice(ctx context.Context, paymentHash string) (transaction *nip47.Transaction, err error) {
|
||||
|
||||
payment := gs.node.Payment(paymentHash)
|
||||
payment := ls.node.Payment(paymentHash)
|
||||
if payment == nil {
|
||||
gs.svc.Logger.Errorf("Couldn't find payment by payment hash: %v", paymentHash)
|
||||
ls.logger.Errorf("Couldn't find payment by payment hash: %v", paymentHash)
|
||||
return nil, errors.New("Payment not found")
|
||||
}
|
||||
|
||||
transaction, err = gs.ldkPaymentToTransaction(payment)
|
||||
transaction, err = ls.ldkPaymentToTransaction(payment)
|
||||
|
||||
if err != nil {
|
||||
gs.svc.Logger.Errorf("Failed to map transaction: %v", err)
|
||||
ls.logger.Errorf("Failed to map transaction: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return transaction, nil
|
||||
}
|
||||
|
||||
func (ls *LDKService) ListTransactions(ctx context.Context, from, until, limit, offset uint64, unpaid bool, invoiceType string) (transactions []Nip47Transaction, err error) {
|
||||
transactions = []Nip47Transaction{}
|
||||
func (ls *LDKService) ListTransactions(ctx context.Context, from, until, limit, offset uint64, unpaid bool, invoiceType string) (transactions []nip47.Transaction, err error) {
|
||||
transactions = []nip47.Transaction{}
|
||||
|
||||
// TODO: support pagination
|
||||
payments := ls.node.ListPayments()
|
||||
|
|
@ -698,7 +704,7 @@ func (ls *LDKService) ListTransactions(ctx context.Context, from, until, limit,
|
|||
transaction, err := ls.ldkPaymentToTransaction(&payment)
|
||||
|
||||
if err != nil {
|
||||
ls.svc.Logger.Errorf("Failed to map transaction: %v", err)
|
||||
ls.logger.Errorf("Failed to map transaction: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
@ -726,7 +732,7 @@ func (ls *LDKService) ListTransactions(ctx context.Context, from, until, limit,
|
|||
if offset < uint64(len(transactions)) {
|
||||
transactions = transactions[offset:]
|
||||
} else {
|
||||
transactions = []Nip47Transaction{}
|
||||
transactions = []nip47.Transaction{}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -734,32 +740,32 @@ func (ls *LDKService) ListTransactions(ctx context.Context, from, until, limit,
|
|||
transactions = transactions[:limit]
|
||||
}
|
||||
|
||||
// ls.svc.Logger.WithField("transactions", transactions).Debug("Listed transactions")
|
||||
// ls.logger.WithField("transactions", transactions).Debug("Listed transactions")
|
||||
|
||||
return transactions, nil
|
||||
}
|
||||
|
||||
func (gs *LDKService) GetInfo(ctx context.Context) (info *lnclient.NodeInfo, err error) {
|
||||
func (ls *LDKService) GetInfo(ctx context.Context) (info *lnclient.NodeInfo, err error) {
|
||||
// TODO: should alias, color be configured in LDK-node? or can we manage them in NWC?
|
||||
// an alias is only needed if the user has public channels and wants their node to be publicly visible?
|
||||
status := gs.node.Status()
|
||||
status := ls.node.Status()
|
||||
return &lnclient.NodeInfo{
|
||||
Alias: "NWC",
|
||||
Color: "#897FFF",
|
||||
Pubkey: gs.node.NodeId(),
|
||||
Network: gs.network,
|
||||
Pubkey: ls.node.NodeId(),
|
||||
Network: ls.network,
|
||||
BlockHeight: status.CurrentBestBlock.Height,
|
||||
BlockHash: status.CurrentBestBlock.BlockHash,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (gs *LDKService) ListChannels(ctx context.Context) ([]lnclient.Channel, error) {
|
||||
func (ls *LDKService) ListChannels(ctx context.Context) ([]lnclient.Channel, error) {
|
||||
|
||||
ldkChannels := gs.node.ListChannels()
|
||||
ldkChannels := ls.node.ListChannels()
|
||||
|
||||
channels := []lnclient.Channel{}
|
||||
|
||||
// gs.svc.Logger.WithFields(logrus.Fields{
|
||||
// gs.logger.WithFields(logrus.Fields{
|
||||
// "channels": ldkChannels,
|
||||
// }).Debug("Listed Channels")
|
||||
|
||||
|
|
@ -786,7 +792,7 @@ func (gs *LDKService) ListChannels(ctx context.Context) ([]lnclient.Channel, err
|
|||
return channels, nil
|
||||
}
|
||||
|
||||
func (gs *LDKService) GetNodeConnectionInfo(ctx context.Context) (nodeConnectionInfo *lnclient.NodeConnectionInfo, err error) {
|
||||
func (ls *LDKService) GetNodeConnectionInfo(ctx context.Context) (nodeConnectionInfo *lnclient.NodeConnectionInfo, err error) {
|
||||
/*addresses := gs.node.ListeningAddresses()
|
||||
if addresses == nil || len(*addresses) < 1 {
|
||||
return nil, errors.New("no available listening addresses")
|
||||
|
|
@ -798,29 +804,29 @@ func (gs *LDKService) GetNodeConnectionInfo(ctx context.Context) (nodeConnection
|
|||
}
|
||||
port, err := strconv.Atoi(parts[1])
|
||||
if err != nil {
|
||||
gs.svc.Logger.WithError(err).Error("ConnectPeer failed")
|
||||
gs.logger.WithError(err).Error("ConnectPeer failed")
|
||||
return nil, err
|
||||
}*/
|
||||
|
||||
return &lnclient.NodeConnectionInfo{
|
||||
Pubkey: gs.node.NodeId(),
|
||||
Pubkey: ls.node.NodeId(),
|
||||
//Address: parts[0],
|
||||
//Port: port,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (gs *LDKService) ConnectPeer(ctx context.Context, connectPeerRequest *lnclient.ConnectPeerRequest) error {
|
||||
err := gs.node.Connect(connectPeerRequest.Pubkey, connectPeerRequest.Address+":"+strconv.Itoa(int(connectPeerRequest.Port)), true)
|
||||
func (ls *LDKService) ConnectPeer(ctx context.Context, connectPeerRequest *lnclient.ConnectPeerRequest) error {
|
||||
err := ls.node.Connect(connectPeerRequest.Pubkey, connectPeerRequest.Address+":"+strconv.Itoa(int(connectPeerRequest.Port)), true)
|
||||
if err != nil {
|
||||
gs.svc.Logger.WithField("request", connectPeerRequest).WithError(err).Error("ConnectPeer failed")
|
||||
ls.logger.WithField("request", connectPeerRequest).WithError(err).Error("ConnectPeer failed")
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gs *LDKService) OpenChannel(ctx context.Context, openChannelRequest *lnclient.OpenChannelRequest) (*lnclient.OpenChannelResponse, error) {
|
||||
peers := gs.node.ListPeers()
|
||||
func (ls *LDKService) OpenChannel(ctx context.Context, openChannelRequest *lnclient.OpenChannelRequest) (*lnclient.OpenChannelResponse, error) {
|
||||
peers := ls.node.ListPeers()
|
||||
var foundPeer *ldk_node.PeerDetails
|
||||
for _, peer := range peers {
|
||||
if peer.NodeId == openChannelRequest.Pubkey {
|
||||
|
|
@ -834,18 +840,18 @@ func (gs *LDKService) OpenChannel(ctx context.Context, openChannelRequest *lncli
|
|||
return nil, errors.New("node is not peered yet")
|
||||
}
|
||||
|
||||
ldkEventSubscription := gs.ldkEventBroadcaster.Subscribe()
|
||||
defer gs.ldkEventBroadcaster.CancelSubscription(ldkEventSubscription)
|
||||
ldkEventSubscription := ls.ldkEventBroadcaster.Subscribe()
|
||||
defer ls.ldkEventBroadcaster.CancelSubscription(ldkEventSubscription)
|
||||
|
||||
gs.svc.Logger.Infof("Opening channel with: %v", foundPeer.NodeId)
|
||||
userChannelId, err := gs.node.ConnectOpenChannel(foundPeer.NodeId, foundPeer.Address, uint64(openChannelRequest.Amount), nil, nil, openChannelRequest.Public)
|
||||
ls.logger.Infof("Opening channel with: %v", foundPeer.NodeId)
|
||||
userChannelId, err := ls.node.ConnectOpenChannel(foundPeer.NodeId, foundPeer.Address, uint64(openChannelRequest.Amount), nil, nil, openChannelRequest.Public)
|
||||
if err != nil {
|
||||
gs.svc.Logger.WithError(err).Error("OpenChannel failed")
|
||||
ls.logger.WithError(err).Error("OpenChannel failed")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// userChannelId allows to locally keep track of the channel (and is also used to close the channel)
|
||||
gs.svc.Logger.Infof("Funded channel: %v", userChannelId)
|
||||
ls.logger.Infof("Funded channel: %v", userChannelId)
|
||||
|
||||
for start := time.Now(); time.Since(start) < time.Second*60; {
|
||||
event := <-ldkEventSubscription
|
||||
|
|
@ -854,7 +860,7 @@ func (gs *LDKService) OpenChannel(ctx context.Context, openChannelRequest *lncli
|
|||
channelClosedEvent, isChannelClosedEvent := (*event).(ldk_node.EventChannelClosed)
|
||||
|
||||
if isChannelClosedEvent {
|
||||
gs.svc.Logger.WithFields(logrus.Fields{
|
||||
ls.logger.WithFields(logrus.Fields{
|
||||
"event": channelClosedEvent,
|
||||
})
|
||||
return nil, fmt.Errorf("failed to open channel: %+v", *channelClosedEvent.Reason)
|
||||
|
|
@ -872,31 +878,31 @@ func (gs *LDKService) OpenChannel(ctx context.Context, openChannelRequest *lncli
|
|||
return nil, errors.New("open channel timeout")
|
||||
}
|
||||
|
||||
func (gs *LDKService) CloseChannel(ctx context.Context, closeChannelRequest *lnclient.CloseChannelRequest) (*lnclient.CloseChannelResponse, error) {
|
||||
gs.svc.Logger.WithFields(logrus.Fields{
|
||||
func (ls *LDKService) CloseChannel(ctx context.Context, closeChannelRequest *lnclient.CloseChannelRequest) (*lnclient.CloseChannelResponse, error) {
|
||||
ls.logger.WithFields(logrus.Fields{
|
||||
"request": closeChannelRequest,
|
||||
}).Info("Closing Channel")
|
||||
// TODO: support passing force option
|
||||
err := gs.node.CloseChannel(closeChannelRequest.ChannelId, closeChannelRequest.NodeId, closeChannelRequest.Force)
|
||||
err := ls.node.CloseChannel(closeChannelRequest.ChannelId, closeChannelRequest.NodeId, closeChannelRequest.Force)
|
||||
if err != nil {
|
||||
gs.svc.Logger.WithError(err).Error("CloseChannel failed")
|
||||
ls.logger.WithError(err).Error("CloseChannel failed")
|
||||
return nil, err
|
||||
}
|
||||
return &lnclient.CloseChannelResponse{}, nil
|
||||
}
|
||||
|
||||
func (gs *LDKService) GetNewOnchainAddress(ctx context.Context) (string, error) {
|
||||
address, err := gs.node.OnchainPayment().NewAddress()
|
||||
func (ls *LDKService) GetNewOnchainAddress(ctx context.Context) (string, error) {
|
||||
address, err := ls.node.OnchainPayment().NewAddress()
|
||||
if err != nil {
|
||||
gs.svc.Logger.WithError(err).Error("NewOnchainAddress failed")
|
||||
ls.logger.WithError(err).Error("NewOnchainAddress failed")
|
||||
return "", err
|
||||
}
|
||||
return address, nil
|
||||
}
|
||||
|
||||
func (gs *LDKService) GetOnchainBalance(ctx context.Context) (*lnclient.OnchainBalanceResponse, error) {
|
||||
balances := gs.node.ListBalances()
|
||||
gs.svc.Logger.WithFields(logrus.Fields{
|
||||
func (ls *LDKService) GetOnchainBalance(ctx context.Context) (*lnclient.OnchainBalanceResponse, error) {
|
||||
balances := ls.node.ListBalances()
|
||||
ls.logger.WithFields(logrus.Fields{
|
||||
"balances": balances,
|
||||
}).Debug("Listed Balances")
|
||||
return &lnclient.OnchainBalanceResponse{
|
||||
|
|
@ -906,33 +912,33 @@ func (gs *LDKService) GetOnchainBalance(ctx context.Context) (*lnclient.OnchainB
|
|||
}, nil
|
||||
}
|
||||
|
||||
func (gs *LDKService) RedeemOnchainFunds(ctx context.Context, toAddress string) (string, error) {
|
||||
txId, err := gs.node.OnchainPayment().SendAllToAddress(toAddress)
|
||||
func (ls *LDKService) RedeemOnchainFunds(ctx context.Context, toAddress string) (string, error) {
|
||||
txId, err := ls.node.OnchainPayment().SendAllToAddress(toAddress)
|
||||
if err != nil {
|
||||
gs.svc.Logger.WithError(err).Error("SendAllToOnchainAddress failed")
|
||||
ls.logger.WithError(err).Error("SendAllToOnchainAddress failed")
|
||||
return "", err
|
||||
}
|
||||
return txId, nil
|
||||
}
|
||||
|
||||
func (ls *LDKService) ResetRouter(key string) error {
|
||||
ls.svc.cfg.SetUpdate(resetRouterKey, key, "")
|
||||
ls.cfg.SetUpdate(resetRouterKey, key, "")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gs *LDKService) SignMessage(ctx context.Context, message string) (string, error) {
|
||||
sign, err := gs.node.SignMessage([]byte(message))
|
||||
func (ls *LDKService) SignMessage(ctx context.Context, message string) (string, error) {
|
||||
sign, err := ls.node.SignMessage([]byte(message))
|
||||
if err != nil {
|
||||
gs.svc.Logger.Errorf("SignMessage failed: %v", err)
|
||||
ls.logger.Errorf("SignMessage failed: %v", err)
|
||||
return "", err
|
||||
}
|
||||
|
||||
return sign, nil
|
||||
}
|
||||
|
||||
func (gs *LDKService) ldkPaymentToTransaction(payment *ldk_node.PaymentDetails) (*Nip47Transaction, error) {
|
||||
// gs.svc.Logger.WithField("payment", payment).Debug("Mapping LDK payment to transaction")
|
||||
func (ls *LDKService) ldkPaymentToTransaction(payment *ldk_node.PaymentDetails) (*nip47.Transaction, error) {
|
||||
// gs.logger.WithField("payment", payment).Debug("Mapping LDK payment to transaction")
|
||||
|
||||
transactionType := "incoming"
|
||||
if payment.Direction == ldk_node.PaymentDirectionOutbound {
|
||||
|
|
@ -954,7 +960,7 @@ func (gs *LDKService) ldkPaymentToTransaction(payment *ldk_node.PaymentDetails)
|
|||
bolt11Invoice = *bolt11PaymentKind.Bolt11Invoice
|
||||
paymentRequest, err := decodepay.Decodepay(strings.ToLower(bolt11Invoice))
|
||||
if err != nil {
|
||||
gs.svc.Logger.WithFields(logrus.Fields{
|
||||
ls.logger.WithFields(logrus.Fields{
|
||||
"bolt11": bolt11Invoice,
|
||||
}).Errorf("Failed to decode bolt11 invoice: %v", err)
|
||||
|
||||
|
|
@ -988,7 +994,7 @@ func (gs *LDKService) ldkPaymentToTransaction(payment *ldk_node.PaymentDetails)
|
|||
fee = *payment.FeeMsat
|
||||
}
|
||||
|
||||
return &Nip47Transaction{
|
||||
return &nip47.Transaction{
|
||||
Type: transactionType,
|
||||
Preimage: preimage,
|
||||
PaymentHash: paymentHash,
|
||||
|
|
@ -1003,28 +1009,28 @@ func (gs *LDKService) ldkPaymentToTransaction(payment *ldk_node.PaymentDetails)
|
|||
}, nil
|
||||
}
|
||||
|
||||
func (gs *LDKService) SendPaymentProbes(ctx context.Context, invoice string) error {
|
||||
err := gs.node.Bolt11Payment().SendProbes(invoice)
|
||||
func (ls *LDKService) SendPaymentProbes(ctx context.Context, invoice string) error {
|
||||
err := ls.node.Bolt11Payment().SendProbes(invoice)
|
||||
if err != nil {
|
||||
gs.svc.Logger.Errorf("Bolt11Payment.SendProbes failed: %v", err)
|
||||
ls.logger.Errorf("Bolt11Payment.SendProbes failed: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gs *LDKService) SendSpontaneousPaymentProbes(ctx context.Context, amountMsat uint64, nodeId string) error {
|
||||
err := gs.node.SpontaneousPayment().SendProbes(amountMsat, nodeId)
|
||||
func (ls *LDKService) SendSpontaneousPaymentProbes(ctx context.Context, amountMsat uint64, nodeId string) error {
|
||||
err := ls.node.SpontaneousPayment().SendProbes(amountMsat, nodeId)
|
||||
if err != nil {
|
||||
gs.svc.Logger.Errorf("SpontaneousPayment.SendProbes failed: %v", err)
|
||||
ls.logger.Errorf("SpontaneousPayment.SendProbes failed: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gs *LDKService) ListPeers(ctx context.Context) ([]lnclient.PeerDetails, error) {
|
||||
peers := gs.node.ListPeers()
|
||||
func (ls *LDKService) ListPeers(ctx context.Context) ([]lnclient.PeerDetails, error) {
|
||||
peers := ls.node.ListPeers()
|
||||
ret := make([]lnclient.PeerDetails, 0, len(peers))
|
||||
for _, peer := range peers {
|
||||
ret = append(ret, lnclient.PeerDetails{
|
||||
|
|
@ -1070,8 +1076,8 @@ func (ls *LDKService) GetNetworkGraph(nodeIds []string) (lnclient.NetworkGraphRe
|
|||
return networkGraph, nil
|
||||
}
|
||||
|
||||
func (gs *LDKService) GetLogOutput(ctx context.Context, maxLen int) ([]byte, error) {
|
||||
config := gs.node.Config()
|
||||
func (ls *LDKService) GetLogOutput(ctx context.Context, maxLen int) ([]byte, error) {
|
||||
config := ls.node.Config()
|
||||
logPath := ""
|
||||
if config.LogDirPath != nil {
|
||||
logPath = *config.LogDirPath
|
||||
|
|
@ -1082,7 +1088,7 @@ func (gs *LDKService) GetLogOutput(ctx context.Context, maxLen int) ([]byte, err
|
|||
|
||||
allLogFiles, err := filepath.Glob(filepath.Join(logPath, "ldk_node_*.log"))
|
||||
if err != nil {
|
||||
gs.svc.Logger.WithError(err).Error("GetLogOutput failed to list log files")
|
||||
ls.logger.WithError(err).Error("GetLogOutput failed to list log files")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
|
@ -1094,17 +1100,17 @@ func (gs *LDKService) GetLogOutput(ctx context.Context, maxLen int) ([]byte, err
|
|||
// naturally sort by date.
|
||||
lastLogFileName := slices.Max(allLogFiles)
|
||||
|
||||
logData, err := ReadFileTail(lastLogFileName, maxLen)
|
||||
logData, err := utils.ReadFileTail(lastLogFileName, maxLen)
|
||||
if err != nil {
|
||||
gs.svc.Logger.WithError(err).Error("GetLogOutput failed to read log file")
|
||||
ls.logger.WithError(err).Error("GetLogOutput failed to read log file")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return logData, nil
|
||||
}
|
||||
|
||||
func (ls *LDKService) logLdkEvent(ctx context.Context, event *ldk_node.Event) {
|
||||
ls.svc.Logger.WithFields(logrus.Fields{
|
||||
func (ls *LDKService) handleLdkEvent(ctx context.Context, event *ldk_node.Event) {
|
||||
ls.logger.WithFields(logrus.Fields{
|
||||
"event": event,
|
||||
}).Info("Received LDK event")
|
||||
|
||||
|
|
@ -1141,7 +1147,7 @@ func (ls *LDKService) logLdkEvent(ctx context.Context, event *ldk_node.Event) {
|
|||
func (ls *LDKService) GetBalances(ctx context.Context) (*lnclient.BalancesResponse, error) {
|
||||
onchainBalance, err := ls.GetOnchainBalance(ctx)
|
||||
if err != nil {
|
||||
ls.svc.Logger.WithError(err).Error("Failed to retrieve onchain balance")
|
||||
ls.logger.WithError(err).Error("Failed to retrieve onchain balance")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
|
@ -1224,3 +1230,7 @@ func (ls *LDKService) GetNodeStatus(ctx context.Context) (nodeStatus *lnclient.N
|
|||
InternalNodeStatus: ls.node.Status(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (ls *LDKService) UpdateLastWalletSyncRequest() {
|
||||
ls.lastWalletSyncRequest = time.Now()
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package ldk
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package lnd
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
|
@ -12,11 +12,12 @@ import (
|
|||
|
||||
decodepay "github.com/nbd-wtf/ln-decodepay"
|
||||
|
||||
"github.com/getAlby/nostr-wallet-connect/lnd"
|
||||
"github.com/getAlby/nostr-wallet-connect/models/lnclient"
|
||||
"github.com/getAlby/nostr-wallet-connect/lnclient"
|
||||
"github.com/getAlby/nostr-wallet-connect/lnclient/lnd/wrapper"
|
||||
"github.com/getAlby/nostr-wallet-connect/nip47"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"gorm.io/gorm"
|
||||
// "gorm.io/gorm"
|
||||
|
||||
"github.com/lightningnetwork/lnd/lnrpc"
|
||||
)
|
||||
|
|
@ -24,8 +25,8 @@ import (
|
|||
// wrap it again :sweat_smile:
|
||||
// todo: drop dependency on lndhub package
|
||||
type LNDService struct {
|
||||
client *lnd.LNDWrapper
|
||||
db *gorm.DB
|
||||
client *wrapper.LNDWrapper
|
||||
// db *gorm.DB
|
||||
Logger *logrus.Logger
|
||||
}
|
||||
|
||||
|
|
@ -37,7 +38,7 @@ func (svc *LNDService) GetBalance(ctx context.Context) (balance int64, err error
|
|||
return int64(resp.LocalBalance.Msat), nil
|
||||
}
|
||||
|
||||
func (svc *LNDService) ListTransactions(ctx context.Context, from, until, limit, offset uint64, unpaid bool, invoiceType string) (transactions []Nip47Transaction, err error) {
|
||||
func (svc *LNDService) ListTransactions(ctx context.Context, from, until, limit, offset uint64, unpaid bool, invoiceType string) (transactions []nip47.Transaction, err error) {
|
||||
// Fetch invoices
|
||||
var invoices []*lnrpc.Invoice
|
||||
if invoiceType == "" || invoiceType == "incoming" {
|
||||
|
|
@ -98,7 +99,7 @@ func (svc *LNDService) ListTransactions(ctx context.Context, from, until, limit,
|
|||
settledAt = &settledAtUnix
|
||||
}
|
||||
|
||||
transaction := Nip47Transaction{
|
||||
transaction := nip47.Transaction{
|
||||
Type: "outgoing",
|
||||
Invoice: payment.PaymentRequest,
|
||||
Preimage: payment.PaymentPreimage,
|
||||
|
|
@ -143,7 +144,7 @@ func (svc *LNDService) ListChannels(ctx context.Context) ([]lnclient.Channel, er
|
|||
return channels, nil
|
||||
}
|
||||
|
||||
func (svc *LNDService) MakeInvoice(ctx context.Context, amount int64, description string, descriptionHash string, expiry int64) (transaction *Nip47Transaction, err error) {
|
||||
func (svc *LNDService) MakeInvoice(ctx context.Context, amount int64, description string, descriptionHash string, expiry int64) (transaction *nip47.Transaction, err error) {
|
||||
var descriptionHashBytes []byte
|
||||
|
||||
if descriptionHash != "" {
|
||||
|
|
@ -174,7 +175,7 @@ func (svc *LNDService) MakeInvoice(ctx context.Context, amount int64, descriptio
|
|||
return transaction, nil
|
||||
}
|
||||
|
||||
func (svc *LNDService) LookupInvoice(ctx context.Context, paymentHash string) (transaction *Nip47Transaction, err error) {
|
||||
func (svc *LNDService) LookupInvoice(ctx context.Context, paymentHash string) (transaction *nip47.Transaction, err error) {
|
||||
paymentHashBytes, err := hex.DecodeString(paymentHash)
|
||||
|
||||
if err != nil || len(paymentHashBytes) != 32 {
|
||||
|
|
@ -193,12 +194,12 @@ func (svc *LNDService) LookupInvoice(ctx context.Context, paymentHash string) (t
|
|||
return transaction, nil
|
||||
}
|
||||
|
||||
func (svc *LNDService) SendPaymentSync(ctx context.Context, payReq string) (*lnclient.Nip47PayInvoiceResponse, error) {
|
||||
func (svc *LNDService) SendPaymentSync(ctx context.Context, payReq string) (*lnclient.PayInvoiceResponse, error) {
|
||||
resp, err := svc.client.SendPaymentSync(ctx, &lnrpc.SendRequest{PaymentRequest: payReq})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &lnclient.Nip47PayInvoiceResponse{
|
||||
return &lnclient.PayInvoiceResponse{
|
||||
Preimage: hex.EncodeToString(resp.PaymentPreimage),
|
||||
}, nil
|
||||
}
|
||||
|
|
@ -302,18 +303,18 @@ func makePreimageHex() ([]byte, error) {
|
|||
return bytes, nil
|
||||
}
|
||||
|
||||
func NewLNDService(ctx context.Context, svc *Service, lndAddress, lndCertHex, lndMacaroonHex string) (result lnclient.LNClient, err error) {
|
||||
func NewLNDService(ctx context.Context, logger *logrus.Logger, lndAddress, lndCertHex, lndMacaroonHex string) (result lnclient.LNClient, err error) {
|
||||
if lndAddress == "" || lndCertHex == "" || lndMacaroonHex == "" {
|
||||
return nil, errors.New("one or more required LND configuration are missing")
|
||||
}
|
||||
|
||||
lndClient, err := lnd.NewLNDclient(lnd.LNDoptions{
|
||||
lndClient, err := wrapper.NewLNDclient(wrapper.LNDoptions{
|
||||
Address: lndAddress,
|
||||
CertHex: lndCertHex,
|
||||
MacaroonHex: lndMacaroonHex,
|
||||
})
|
||||
if err != nil {
|
||||
svc.Logger.Errorf("Failed to create new LND client %v", err)
|
||||
logger.Errorf("Failed to create new LND client %v", err)
|
||||
return nil, err
|
||||
}
|
||||
info, err := lndClient.GetInfo(ctx, &lnrpc.GetInfoRequest{})
|
||||
|
|
@ -321,9 +322,9 @@ func NewLNDService(ctx context.Context, svc *Service, lndAddress, lndCertHex, ln
|
|||
return nil, err
|
||||
}
|
||||
|
||||
lndService := &LNDService{client: lndClient, Logger: svc.Logger, db: svc.db}
|
||||
lndService := &LNDService{client: lndClient, Logger: logger}
|
||||
|
||||
svc.Logger.Infof("Connected to LND - alias %s", info.Alias)
|
||||
logger.Infof("Connected to LND - alias %s", info.Alias)
|
||||
|
||||
return lndService, nil
|
||||
}
|
||||
|
|
@ -406,7 +407,7 @@ func (svc *LNDService) GetBalances(ctx context.Context) (*lnclient.BalancesRespo
|
|||
}, nil
|
||||
}
|
||||
|
||||
func lndInvoiceToTransaction(invoice *lnrpc.Invoice) *Nip47Transaction {
|
||||
func lndInvoiceToTransaction(invoice *lnrpc.Invoice) *nip47.Transaction {
|
||||
var settledAt *int64
|
||||
var preimage string
|
||||
if invoice.State == lnrpc.Invoice_SETTLED {
|
||||
|
|
@ -420,7 +421,7 @@ func lndInvoiceToTransaction(invoice *lnrpc.Invoice) *Nip47Transaction {
|
|||
expiresAt = &expiresAtUnix
|
||||
}
|
||||
|
||||
return &Nip47Transaction{
|
||||
return &nip47.Transaction{
|
||||
Type: "incoming",
|
||||
Invoice: invoice.PaymentRequest,
|
||||
Description: invoice.Memo,
|
||||
|
|
@ -451,3 +452,5 @@ func (svc *LNDService) GetNodeStatus(ctx context.Context) (nodeStatus *lnclient.
|
|||
func (svc *LNDService) GetNetworkGraph(nodeIds []string) (lnclient.NetworkGraphResponse, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (svc *LNDService) UpdateLastWalletSyncRequest() {}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package lnd
|
||||
package wrapper
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package lnd
|
||||
package wrapper
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
|
@ -42,7 +42,7 @@ type NodeConnectionInfo struct {
|
|||
}
|
||||
|
||||
type LNClient interface {
|
||||
SendPaymentSync(ctx context.Context, payReq string) (*Nip47PayInvoiceResponse, error)
|
||||
SendPaymentSync(ctx context.Context, payReq string) (*PayInvoiceResponse, error)
|
||||
SendKeysend(ctx context.Context, amount int64, destination, preimage string, customRecords []TLVRecord) (preImage string, err error)
|
||||
GetBalance(ctx context.Context) (balance int64, err error)
|
||||
GetInfo(ctx context.Context) (info *NodeInfo, err error)
|
||||
|
|
@ -68,6 +68,7 @@ type LNClient interface {
|
|||
SignMessage(ctx context.Context, message string) (string, error)
|
||||
GetStorageDir() (string, error)
|
||||
GetNetworkGraph(nodeIds []string) (NetworkGraphResponse, error)
|
||||
UpdateLastWalletSyncRequest()
|
||||
}
|
||||
|
||||
type Channel struct {
|
||||
|
|
@ -133,7 +134,7 @@ type LightningBalanceResponse struct {
|
|||
NextMaxReceivableMPP int64 `json:"nextMaxReceivableMPP"`
|
||||
}
|
||||
|
||||
type Nip47PayInvoiceResponse struct {
|
||||
type PayInvoiceResponse struct {
|
||||
Preimage string `json:"preimage"`
|
||||
Fee *uint64 `json:"fee"`
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package phoenixd
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
|
@ -12,10 +12,10 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/getAlby/nostr-wallet-connect/models/lnclient"
|
||||
"github.com/getAlby/nostr-wallet-connect/lnclient"
|
||||
"github.com/getAlby/nostr-wallet-connect/nip47"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type InvoiceResponse struct {
|
||||
|
|
@ -67,13 +67,12 @@ type BalanceResponse struct {
|
|||
type PhoenixService struct {
|
||||
Address string
|
||||
Authorization string
|
||||
db *gorm.DB
|
||||
Logger *logrus.Logger
|
||||
}
|
||||
|
||||
func NewPhoenixService(svc *Service, address string, authorization string) (result lnclient.LNClient, err error) {
|
||||
func NewPhoenixService(logger *logrus.Logger, address string, authorization string) (result lnclient.LNClient, err error) {
|
||||
authorizationBase64 := b64.StdEncoding.EncodeToString([]byte(":" + authorization))
|
||||
phoenixService := &PhoenixService{Logger: svc.Logger, db: svc.db, Address: address, Authorization: authorizationBase64}
|
||||
phoenixService := &PhoenixService{Logger: logger, Address: address, Authorization: authorizationBase64}
|
||||
|
||||
return phoenixService, nil
|
||||
}
|
||||
|
|
@ -122,7 +121,7 @@ func (svc *PhoenixService) GetBalances(ctx context.Context) (*lnclient.BalancesR
|
|||
}, nil
|
||||
}
|
||||
|
||||
func (svc *PhoenixService) ListTransactions(ctx context.Context, from, until, limit, offset uint64, unpaid bool, invoiceType string) (transactions []Nip47Transaction, err error) {
|
||||
func (svc *PhoenixService) ListTransactions(ctx context.Context, from, until, limit, offset uint64, unpaid bool, invoiceType string) (transactions []nip47.Transaction, err error) {
|
||||
incomingQuery := url.Values{}
|
||||
if from != 0 {
|
||||
incomingQuery.Add("from", strconv.FormatUint(from*1000, 10))
|
||||
|
|
@ -160,14 +159,14 @@ func (svc *PhoenixService) ListTransactions(ctx context.Context, from, until, li
|
|||
if err := json.NewDecoder(incomingResp.Body).Decode(&incomingPayments); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
transactions = []Nip47Transaction{}
|
||||
transactions = []nip47.Transaction{}
|
||||
for _, invoice := range incomingPayments {
|
||||
var settledAt *int64
|
||||
if invoice.CompletedAt != 0 {
|
||||
settledAtUnix := time.UnixMilli(invoice.CompletedAt).Unix()
|
||||
settledAt = &settledAtUnix
|
||||
}
|
||||
transaction := Nip47Transaction{
|
||||
transaction := nip47.Transaction{
|
||||
Type: "incoming",
|
||||
Invoice: invoice.Invoice,
|
||||
Preimage: invoice.Preimage,
|
||||
|
|
@ -223,7 +222,7 @@ func (svc *PhoenixService) ListTransactions(ctx context.Context, from, until, li
|
|||
settledAtUnix := time.UnixMilli(invoice.CompletedAt).Unix()
|
||||
settledAt = &settledAtUnix
|
||||
}
|
||||
transaction := Nip47Transaction{
|
||||
transaction := nip47.Transaction{
|
||||
Type: "outgoing",
|
||||
Invoice: invoice.Invoice,
|
||||
Preimage: invoice.Preimage,
|
||||
|
|
@ -276,7 +275,7 @@ func (svc *PhoenixService) ListChannels(ctx context.Context) ([]lnclient.Channel
|
|||
return channels, nil
|
||||
}
|
||||
|
||||
func (svc *PhoenixService) MakeInvoice(ctx context.Context, amount int64, description string, descriptionHash string, expiry int64) (transaction *Nip47Transaction, err error) {
|
||||
func (svc *PhoenixService) MakeInvoice(ctx context.Context, amount int64, description string, descriptionHash string, expiry int64) (transaction *nip47.Transaction, err error) {
|
||||
form := url.Values{}
|
||||
amountSat := strconv.FormatInt(amount/1000, 10)
|
||||
form.Add("amountSat", amountSat)
|
||||
|
|
@ -313,7 +312,7 @@ func (svc *PhoenixService) MakeInvoice(ctx context.Context, amount int64, descri
|
|||
}
|
||||
expiresAt := time.Now().Add(1 * time.Hour).Unix()
|
||||
|
||||
tx := &Nip47Transaction{
|
||||
tx := &nip47.Transaction{
|
||||
Type: "incoming",
|
||||
Invoice: invoiceRes.Serialized,
|
||||
Preimage: "",
|
||||
|
|
@ -327,7 +326,7 @@ func (svc *PhoenixService) MakeInvoice(ctx context.Context, amount int64, descri
|
|||
return tx, nil
|
||||
}
|
||||
|
||||
func (svc *PhoenixService) LookupInvoice(ctx context.Context, paymentHash string) (transaction *Nip47Transaction, err error) {
|
||||
func (svc *PhoenixService) LookupInvoice(ctx context.Context, paymentHash string) (transaction *nip47.Transaction, err error) {
|
||||
req, err := http.NewRequest(http.MethodGet, svc.Address+"/payments/incoming/"+paymentHash, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -350,7 +349,7 @@ func (svc *PhoenixService) LookupInvoice(ctx context.Context, paymentHash string
|
|||
settledAtUnix := time.UnixMilli(invoiceRes.CompletedAt).Unix()
|
||||
settledAt = &settledAtUnix
|
||||
}
|
||||
transaction = &Nip47Transaction{
|
||||
transaction = &nip47.Transaction{
|
||||
Type: "incoming",
|
||||
Invoice: invoiceRes.Invoice,
|
||||
Preimage: invoiceRes.Preimage,
|
||||
|
|
@ -364,7 +363,7 @@ func (svc *PhoenixService) LookupInvoice(ctx context.Context, paymentHash string
|
|||
return transaction, nil
|
||||
}
|
||||
|
||||
func (svc *PhoenixService) SendPaymentSync(ctx context.Context, payReq string) (*lnclient.Nip47PayInvoiceResponse, error) {
|
||||
func (svc *PhoenixService) SendPaymentSync(ctx context.Context, payReq string) (*lnclient.PayInvoiceResponse, error) {
|
||||
form := url.Values{}
|
||||
form.Add("invoice", payReq)
|
||||
req, err := http.NewRequest(http.MethodPost, svc.Address+"/payinvoice", strings.NewReader(form.Encode()))
|
||||
|
|
@ -386,7 +385,7 @@ func (svc *PhoenixService) SendPaymentSync(ctx context.Context, payReq string) (
|
|||
}
|
||||
|
||||
fee := uint64(payRes.RoutingFeeSat) * 1000
|
||||
return &lnclient.Nip47PayInvoiceResponse{
|
||||
return &lnclient.PayInvoiceResponse{
|
||||
Preimage: payRes.PaymentPreimage,
|
||||
Fee: &fee,
|
||||
}, nil
|
||||
|
|
@ -480,3 +479,5 @@ func (svc *PhoenixService) GetStorageDir() (string, error) {
|
|||
func (svc *PhoenixService) GetNetworkGraph(nodeIds []string) (lnclient.NetworkGraphResponse, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (svc *PhoenixService) UpdateLastWalletSyncRequest() {}
|
||||
621
lsp/lsp_service.go
Normal file
621
lsp/lsp_service.go
Normal file
|
|
@ -0,0 +1,621 @@
|
|||
package lsp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/getAlby/nostr-wallet-connect/lnclient"
|
||||
"github.com/getAlby/nostr-wallet-connect/service"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type lspService struct {
|
||||
svc service.Service
|
||||
logger *logrus.Logger
|
||||
}
|
||||
|
||||
type lspConnectionInfo struct {
|
||||
Pubkey string
|
||||
Address string
|
||||
Port uint16
|
||||
}
|
||||
|
||||
func NewLSPService(svc service.Service, logger *logrus.Logger) *lspService {
|
||||
return &lspService{
|
||||
svc: svc,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
func (ls *lspService) NewInstantChannelInvoice(ctx context.Context, request *NewInstantChannelInvoiceRequest) (*NewInstantChannelInvoiceResponse, error) {
|
||||
var selectedLsp LSP
|
||||
switch request.LSP {
|
||||
case "VOLTAGE":
|
||||
selectedLsp = VoltageLSP()
|
||||
case "OLYMPUS_FLOW_2_0":
|
||||
selectedLsp = OlympusLSP()
|
||||
case "OLYMPUS_MUTINYNET_FLOW_2_0":
|
||||
selectedLsp = OlympusMutinynetFlowLSP()
|
||||
case "OLYMPUS_MUTINYNET_LSPS1":
|
||||
selectedLsp = OlympusMutinynetLSPS1LSP()
|
||||
case "ALBY":
|
||||
selectedLsp = AlbyPlebsLSP()
|
||||
case "ALBY_MUTINYNET":
|
||||
selectedLsp = AlbyMutinynetPlebsLSP()
|
||||
default:
|
||||
return nil, errors.New("unknown LSP")
|
||||
}
|
||||
|
||||
if ls.svc.GetLNClient() == nil {
|
||||
return nil, errors.New("LNClient not started")
|
||||
}
|
||||
|
||||
ls.logger.Infoln("Requesting LSP info")
|
||||
|
||||
var lspInfo *lspConnectionInfo
|
||||
var err error
|
||||
switch selectedLsp.LspType {
|
||||
case LSP_TYPE_FLOW_2_0:
|
||||
fallthrough
|
||||
case LSP_TYPE_PMLSP:
|
||||
lspInfo, err = ls.getFlowLSPInfo(selectedLsp.Url + "/info")
|
||||
|
||||
case LSP_TYPE_LSPS1:
|
||||
lspInfo, err = ls.getLSPS1LSPInfo(selectedLsp.Url + "/get_info")
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported LSP type: %v", selectedLsp.LspType)
|
||||
}
|
||||
if err != nil {
|
||||
ls.logger.WithError(err).Error("Failed to request LSP info")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ls.logger.Infoln("Requesting own node info")
|
||||
|
||||
nodeInfo, err := ls.svc.GetLNClient().GetInfo(ctx)
|
||||
if err != nil {
|
||||
ls.logger.WithError(err).WithFields(logrus.Fields{
|
||||
"url": selectedLsp.Url,
|
||||
}).Error("Failed to request own node info", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ls.logger.WithField("lspInfo", lspInfo).Info("Connecting to LSP node as a peer")
|
||||
|
||||
err = ls.svc.GetLNClient().ConnectPeer(ctx, &lnclient.ConnectPeerRequest{
|
||||
Pubkey: lspInfo.Pubkey,
|
||||
Address: lspInfo.Address,
|
||||
Port: lspInfo.Port,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
ls.logger.WithError(err).Error("Failed to connect to peer")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
invoice := ""
|
||||
var fee uint64 = 0
|
||||
|
||||
switch selectedLsp.LspType {
|
||||
case LSP_TYPE_FLOW_2_0:
|
||||
invoice, fee, err = ls.requestFlow20WrappedInvoice(ctx, &selectedLsp, request.Amount, nodeInfo.Pubkey)
|
||||
case LSP_TYPE_PMLSP:
|
||||
invoice, fee, err = ls.requestPMLSPInvoice(&selectedLsp, request.Amount, nodeInfo.Pubkey)
|
||||
case LSP_TYPE_LSPS1:
|
||||
invoice, fee, err = ls.requestLSPS1Invoice(ctx, &selectedLsp, request.Amount, nodeInfo.Pubkey)
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported LSP type: %v", selectedLsp.LspType)
|
||||
}
|
||||
if err != nil {
|
||||
ls.logger.WithError(err).Error("Failed to request invoice")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
newChannelResponse := &NewInstantChannelInvoiceResponse{
|
||||
Invoice: invoice,
|
||||
Fee: fee,
|
||||
}
|
||||
|
||||
ls.logger.WithFields(logrus.Fields{
|
||||
"newChannelResponse": newChannelResponse,
|
||||
}).Info("New Channel response")
|
||||
|
||||
return newChannelResponse, nil
|
||||
}
|
||||
|
||||
func (ls *lspService) getLSPS1LSPInfo(url string) (*lspConnectionInfo, error) {
|
||||
type LSPS1LSPInfo struct {
|
||||
// TODO: implement options
|
||||
Options interface{} `json:"options"`
|
||||
URIs []string `json:"uris"`
|
||||
}
|
||||
var lsps1LspInfo LSPS1LSPInfo
|
||||
client := http.Client{
|
||||
Timeout: time.Second * 10,
|
||||
}
|
||||
req, err := http.NewRequest(http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
ls.logger.WithError(err).WithFields(logrus.Fields{
|
||||
"url": url,
|
||||
}).Error("Failed to create lsp info request")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res, err := client.Do(req)
|
||||
if err != nil {
|
||||
ls.logger.WithError(err).WithFields(logrus.Fields{
|
||||
"url": url,
|
||||
}).Error("Failed to request lsp info")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer res.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
ls.logger.WithError(err).WithFields(logrus.Fields{
|
||||
"url": url,
|
||||
}).Error("Failed to read response body")
|
||||
return nil, errors.New("failed to read response body")
|
||||
}
|
||||
|
||||
err = json.Unmarshal(body, &lsps1LspInfo)
|
||||
if err != nil {
|
||||
ls.logger.WithError(err).WithFields(logrus.Fields{
|
||||
"url": url,
|
||||
}).Error("Failed to deserialize json")
|
||||
return nil, fmt.Errorf("failed to deserialize json %s %s", url, string(body))
|
||||
}
|
||||
|
||||
uri := lsps1LspInfo.URIs[0]
|
||||
|
||||
// make sure it's a valid IPv4 URI
|
||||
regex := regexp.MustCompile(`^([0-9a-f]+)@([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+):([0-9]+)$`)
|
||||
parts := regex.FindStringSubmatch(uri)
|
||||
ls.logger.WithField("parts", parts).Info("Split URI")
|
||||
if parts == nil || len(parts) != 4 {
|
||||
ls.logger.WithField("parts", parts).Info("Unsupported URI")
|
||||
return nil, errors.New("could not decode LSP URI")
|
||||
}
|
||||
|
||||
port, err := strconv.Atoi(parts[3])
|
||||
if err != nil {
|
||||
ls.logger.WithField("port", parts[3]).WithError(err).Info("Failed to decode port number")
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &lspConnectionInfo{
|
||||
Pubkey: parts[1],
|
||||
Address: parts[2],
|
||||
Port: uint16(port),
|
||||
}, nil
|
||||
}
|
||||
func (ls *lspService) getFlowLSPInfo(url string) (*lspConnectionInfo, error) {
|
||||
type FlowLSPConnectionMethod struct {
|
||||
Address string `json:"address"`
|
||||
Port uint16 `json:"port"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
type FlowLSPInfo struct {
|
||||
Pubkey string `json:"pubkey"`
|
||||
ConnectionMethods []FlowLSPConnectionMethod `json:"connection_methods"`
|
||||
}
|
||||
var flowLspInfo FlowLSPInfo
|
||||
client := http.Client{
|
||||
Timeout: time.Second * 10,
|
||||
}
|
||||
req, err := http.NewRequest(http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
ls.logger.WithError(err).WithFields(logrus.Fields{
|
||||
"url": url,
|
||||
}).Error("Failed to create lsp info request")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res, err := client.Do(req)
|
||||
if err != nil {
|
||||
ls.logger.WithError(err).WithFields(logrus.Fields{
|
||||
"url": url,
|
||||
}).Error("Failed to request lsp info")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer res.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
ls.logger.WithError(err).WithFields(logrus.Fields{
|
||||
"url": url,
|
||||
}).Error("Failed to read response body")
|
||||
return nil, errors.New("failed to read response body")
|
||||
}
|
||||
|
||||
err = json.Unmarshal(body, &flowLspInfo)
|
||||
if err != nil {
|
||||
ls.logger.WithError(err).WithFields(logrus.Fields{
|
||||
"url": url,
|
||||
}).Error("Failed to deserialize json")
|
||||
return nil, fmt.Errorf("failed to deserialize json %s %s", url, string(body))
|
||||
}
|
||||
|
||||
ipIndex := -1
|
||||
for i, cm := range flowLspInfo.ConnectionMethods {
|
||||
if strings.HasPrefix(cm.Type, "ip") {
|
||||
ipIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if ipIndex == -1 {
|
||||
ls.logger.Error("No ipv4/ipv6 connection method found in LSP info")
|
||||
return nil, errors.New("unexpected LSP connection method")
|
||||
}
|
||||
|
||||
return &lspConnectionInfo{
|
||||
Pubkey: flowLspInfo.Pubkey,
|
||||
Address: flowLspInfo.ConnectionMethods[ipIndex].Address,
|
||||
Port: flowLspInfo.ConnectionMethods[ipIndex].Port,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (ls *lspService) requestFlow20WrappedInvoice(ctx context.Context, selectedLsp *LSP, amount uint64, pubkey string) (invoice string, fee uint64, err error) {
|
||||
ls.logger.Infoln("Requesting fee information")
|
||||
|
||||
type FeeRequest struct {
|
||||
AmountMsat uint64 `json:"amount_msat"`
|
||||
Pubkey string `json:"pubkey"`
|
||||
}
|
||||
type FeeResponse struct {
|
||||
FeeAmountMsat uint64 `json:"fee_amount_msat"`
|
||||
Id string `json:"id"`
|
||||
}
|
||||
|
||||
var feeResponse FeeResponse
|
||||
{
|
||||
client := http.Client{
|
||||
Timeout: time.Second * 10,
|
||||
}
|
||||
payloadBytes, err := json.Marshal(FeeRequest{
|
||||
AmountMsat: amount * 1000,
|
||||
Pubkey: pubkey,
|
||||
})
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
bodyReader := bytes.NewReader(payloadBytes)
|
||||
|
||||
req, err := http.NewRequest(http.MethodPost, selectedLsp.Url+"/fee", bodyReader)
|
||||
if err != nil {
|
||||
ls.logger.WithError(err).WithFields(logrus.Fields{
|
||||
"url": selectedLsp.Url,
|
||||
}).Error("Failed to create lsp fee request")
|
||||
return "", 0, err
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
res, err := client.Do(req)
|
||||
if err != nil {
|
||||
ls.logger.WithError(err).WithFields(logrus.Fields{
|
||||
"url": selectedLsp.Url,
|
||||
}).Error("Failed to request lsp fee")
|
||||
return "", 0, err
|
||||
}
|
||||
|
||||
defer res.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
ls.logger.WithError(err).WithFields(logrus.Fields{
|
||||
"url": selectedLsp.Url,
|
||||
}).Error("Failed to read response body")
|
||||
return "", 0, errors.New("failed to read response body")
|
||||
}
|
||||
|
||||
if res.StatusCode >= 300 {
|
||||
ls.logger.WithFields(logrus.Fields{
|
||||
"body": string(body),
|
||||
"statusCode": res.StatusCode,
|
||||
}).Error("fee endpoint returned non-success code")
|
||||
return "", 0, fmt.Errorf("fee endpoint returned non-success code: %s", string(body))
|
||||
}
|
||||
|
||||
err = json.Unmarshal(body, &feeResponse)
|
||||
if err != nil {
|
||||
ls.logger.WithError(err).WithFields(logrus.Fields{
|
||||
"url": selectedLsp.Url,
|
||||
}).Error("Failed to deserialize json")
|
||||
return "", 0, fmt.Errorf("failed to deserialize json %s %s", selectedLsp.Url, string(body))
|
||||
}
|
||||
|
||||
ls.logger.WithError(err).WithFields(logrus.Fields{
|
||||
"url": selectedLsp.Url,
|
||||
"feeResponse": feeResponse,
|
||||
}).Info("Got fee response")
|
||||
if feeResponse.Id == "" {
|
||||
ls.logger.WithError(err).WithFields(logrus.Fields{
|
||||
"feeResponse": feeResponse,
|
||||
}).Error("No fee id in fee response")
|
||||
return "", 0, fmt.Errorf("no fee id in fee response %v", feeResponse)
|
||||
}
|
||||
fee = feeResponse.FeeAmountMsat / 1000
|
||||
}
|
||||
|
||||
// because we don't want the sender to pay the fee
|
||||
// see: https://docs.voltage.cloud/voltage-lsp#gqBqV
|
||||
makeInvoiceResponse, err := ls.svc.GetLNClient().MakeInvoice(ctx, int64(amount)*1000-int64(feeResponse.FeeAmountMsat), "", "", 60*60)
|
||||
if err != nil {
|
||||
ls.logger.WithError(err).Error("Failed to request own invoice")
|
||||
return "", 0, fmt.Errorf("failed to request own invoice %v", err)
|
||||
}
|
||||
|
||||
type ProposalRequest struct {
|
||||
Bolt11 string `json:"bolt11"`
|
||||
FeeId string `json:"fee_id"`
|
||||
}
|
||||
type ProposalResponse struct {
|
||||
Bolt11 string `json:"jit_bolt11"`
|
||||
}
|
||||
|
||||
ls.logger.Infoln("Proposing invoice")
|
||||
|
||||
var proposalResponse ProposalResponse
|
||||
{
|
||||
client := http.Client{
|
||||
Timeout: time.Second * 10,
|
||||
}
|
||||
payloadBytes, err := json.Marshal(ProposalRequest{
|
||||
Bolt11: makeInvoiceResponse.Invoice,
|
||||
FeeId: feeResponse.Id,
|
||||
})
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
bodyReader := bytes.NewReader(payloadBytes)
|
||||
|
||||
req, err := http.NewRequest(http.MethodPost, selectedLsp.Url+"/proposal", bodyReader)
|
||||
if err != nil {
|
||||
ls.logger.WithError(err).WithFields(logrus.Fields{
|
||||
"url": selectedLsp.Url,
|
||||
}).Error("Failed to create lsp fee request")
|
||||
return "", 0, err
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
res, err := client.Do(req)
|
||||
if err != nil {
|
||||
ls.logger.WithError(err).WithFields(logrus.Fields{
|
||||
"url": selectedLsp.Url,
|
||||
}).Error("Failed to request lsp fee")
|
||||
return "", 0, err
|
||||
}
|
||||
|
||||
defer res.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
ls.logger.WithError(err).WithFields(logrus.Fields{
|
||||
"url": selectedLsp.Url,
|
||||
}).Error("Failed to read response body")
|
||||
return "", 0, errors.New("failed to read response body")
|
||||
}
|
||||
|
||||
if res.StatusCode >= 300 {
|
||||
ls.logger.WithFields(logrus.Fields{
|
||||
"body": string(body),
|
||||
"statusCode": res.StatusCode,
|
||||
}).Error("proposal endpoint returned non-success code")
|
||||
return "", 0, fmt.Errorf("proposal endpoint returned non-success code: %s", string(body))
|
||||
}
|
||||
|
||||
err = json.Unmarshal(body, &proposalResponse)
|
||||
if err != nil {
|
||||
ls.logger.WithError(err).WithFields(logrus.Fields{
|
||||
"url": selectedLsp.Url,
|
||||
}).Error("Failed to deserialize json")
|
||||
return "", 0, fmt.Errorf("failed to deserialize json %s %s", selectedLsp.Url, string(body))
|
||||
}
|
||||
ls.logger.WithField("proposalResponse", proposalResponse).Info("Got proposal response")
|
||||
if proposalResponse.Bolt11 == "" {
|
||||
ls.logger.WithError(err).WithFields(logrus.Fields{
|
||||
"url": selectedLsp.Url,
|
||||
"proposalResponse": proposalResponse,
|
||||
}).Error("No bolt11 in proposal response")
|
||||
return "", 0, fmt.Errorf("no bolt11 in proposal response %v", proposalResponse)
|
||||
}
|
||||
}
|
||||
invoice = proposalResponse.Bolt11
|
||||
|
||||
return invoice, fee, nil
|
||||
}
|
||||
|
||||
func (ls *lspService) requestPMLSPInvoice(selectedLsp *LSP, amount uint64, pubkey string) (invoice string, fee uint64, err error) {
|
||||
type NewInstantChannelRequest struct {
|
||||
Amount uint64 `json:"amount"`
|
||||
Pubkey string `json:"pubkey"`
|
||||
}
|
||||
|
||||
client := http.Client{
|
||||
Timeout: time.Second * 10,
|
||||
}
|
||||
payloadBytes, err := json.Marshal(NewInstantChannelRequest{
|
||||
Amount: amount,
|
||||
Pubkey: pubkey,
|
||||
})
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
bodyReader := bytes.NewReader(payloadBytes)
|
||||
|
||||
req, err := http.NewRequest(http.MethodPost, selectedLsp.Url+"/new-channel", bodyReader)
|
||||
if err != nil {
|
||||
ls.logger.WithError(err).WithFields(logrus.Fields{
|
||||
"url": selectedLsp.Url,
|
||||
}).Error("Failed to create new channel request")
|
||||
return "", 0, err
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
res, err := client.Do(req)
|
||||
if err != nil {
|
||||
ls.logger.WithError(err).WithFields(logrus.Fields{
|
||||
"url": selectedLsp.Url,
|
||||
}).Error("Failed to request new channel invoice")
|
||||
return "", 0, err
|
||||
}
|
||||
|
||||
defer res.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
ls.logger.WithError(err).WithFields(logrus.Fields{
|
||||
"url": selectedLsp.Url,
|
||||
}).Error("Failed to read response body")
|
||||
return "", 0, errors.New("failed to read response body")
|
||||
}
|
||||
|
||||
if res.StatusCode >= 300 {
|
||||
ls.logger.WithFields(logrus.Fields{
|
||||
"body": string(body),
|
||||
"statusCode": res.StatusCode,
|
||||
}).Error("new-channel endpoint returned non-success code")
|
||||
return "", 0, fmt.Errorf("new-channel endpoint returned non-success code: %s", string(body))
|
||||
}
|
||||
|
||||
var newChannelResponse NewInstantChannelResponse
|
||||
|
||||
err = json.Unmarshal(body, &newChannelResponse)
|
||||
if err != nil {
|
||||
ls.logger.WithError(err).WithFields(logrus.Fields{
|
||||
"url": selectedLsp.Url,
|
||||
}).Error("Failed to deserialize json")
|
||||
return "", 0, fmt.Errorf("failed to deserialize json %s %s", selectedLsp.Url, string(body))
|
||||
}
|
||||
|
||||
invoice = newChannelResponse.Invoice
|
||||
fee = newChannelResponse.FeeAmountMsat / 1000
|
||||
|
||||
return invoice, fee, nil
|
||||
}
|
||||
|
||||
func (ls *lspService) requestLSPS1Invoice(ctx context.Context, selectedLsp *LSP, amount uint64, pubkey string) (invoice string, fee uint64, err error) {
|
||||
client := http.Client{
|
||||
Timeout: time.Second * 10,
|
||||
}
|
||||
|
||||
type NewLSPS1ChannelRequest struct {
|
||||
PublicKey string `json:"public_key"`
|
||||
LSPBalanceSat string `json:"lsp_balance_sat"`
|
||||
ClientBalanceSat string `json:"client_balance_sat"`
|
||||
RequiredChannelConfirmations uint64 `json:"required_channel_confirmations"`
|
||||
FundingConfirmsWithinBlocks uint64 `json:"funding_confirms_within_blocks"`
|
||||
ChannelExpiryBlocks uint64 `json:"channel_expiry_blocks"`
|
||||
Token string `json:"token"`
|
||||
RefundOnchainAddress string `json:"refund_onchain_address"`
|
||||
AnnounceChannel bool `json:"announce_channel"`
|
||||
}
|
||||
|
||||
refundAddress, err := ls.svc.GetLNClient().GetNewOnchainAddress(ctx)
|
||||
if err != nil {
|
||||
ls.logger.WithError(err).Error("Failed to request onchain address")
|
||||
return "", 0, err
|
||||
}
|
||||
|
||||
newLSPS1ChannelRequest := NewLSPS1ChannelRequest{
|
||||
PublicKey: pubkey,
|
||||
LSPBalanceSat: strconv.FormatUint(amount, 10),
|
||||
ClientBalanceSat: "0",
|
||||
RequiredChannelConfirmations: 0,
|
||||
FundingConfirmsWithinBlocks: 6,
|
||||
ChannelExpiryBlocks: 13000, // TODO: this should be customizable
|
||||
Token: "",
|
||||
RefundOnchainAddress: refundAddress,
|
||||
AnnounceChannel: false, // TODO: this should be customizable
|
||||
}
|
||||
|
||||
payloadBytes, err := json.Marshal(newLSPS1ChannelRequest)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
bodyReader := bytes.NewReader(payloadBytes)
|
||||
|
||||
req, err := http.NewRequest(http.MethodPost, selectedLsp.Url+"/create_order", bodyReader)
|
||||
if err != nil {
|
||||
ls.logger.WithError(err).WithFields(logrus.Fields{
|
||||
"url": selectedLsp.Url,
|
||||
}).Error("Failed to create new channel request")
|
||||
return "", 0, err
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
res, err := client.Do(req)
|
||||
if err != nil {
|
||||
ls.logger.WithError(err).WithFields(logrus.Fields{
|
||||
"url": selectedLsp.Url,
|
||||
}).Error("Failed to request new channel invoice")
|
||||
return "", 0, err
|
||||
}
|
||||
|
||||
defer res.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
ls.logger.WithError(err).WithFields(logrus.Fields{
|
||||
"url": selectedLsp.Url,
|
||||
}).Error("Failed to read response body")
|
||||
return "", 0, errors.New("failed to read response body")
|
||||
}
|
||||
|
||||
if res.StatusCode >= 300 {
|
||||
ls.logger.WithFields(logrus.Fields{
|
||||
"newLSPS1ChannelRequest": newLSPS1ChannelRequest,
|
||||
"body": string(body),
|
||||
"statusCode": res.StatusCode,
|
||||
}).Error("create_order endpoint returned non-success code")
|
||||
return "", 0, fmt.Errorf("create_order endpoint returned non-success code: %s", string(body))
|
||||
}
|
||||
|
||||
type NewLSPS1ChannelPayment struct {
|
||||
LightningInvoice string `json:"lightning_invoice"`
|
||||
FeeTotalSat string `json:"fee_total_sat"`
|
||||
}
|
||||
type NewLSPS1ChannelResponse struct {
|
||||
Payment NewLSPS1ChannelPayment `json:"payment"`
|
||||
}
|
||||
|
||||
var newChannelResponse NewLSPS1ChannelResponse
|
||||
|
||||
err = json.Unmarshal(body, &newChannelResponse)
|
||||
if err != nil {
|
||||
ls.logger.WithError(err).WithFields(logrus.Fields{
|
||||
"url": selectedLsp.Url,
|
||||
}).Error("Failed to deserialize json")
|
||||
return "", 0, fmt.Errorf("failed to deserialize json %s %s", selectedLsp.Url, string(body))
|
||||
}
|
||||
|
||||
invoice = newChannelResponse.Payment.LightningInvoice
|
||||
fee, err = strconv.ParseUint(newChannelResponse.Payment.FeeTotalSat, 10, 64)
|
||||
if err != nil {
|
||||
ls.logger.WithError(err).WithFields(logrus.Fields{
|
||||
"url": selectedLsp.Url,
|
||||
}).Error("Failed to parse fee")
|
||||
return "", 0, fmt.Errorf("failed to parse fee %v", err)
|
||||
}
|
||||
|
||||
return invoice, fee, nil
|
||||
}
|
||||
|
|
@ -1,6 +1,9 @@
|
|||
// TODO: move to lsp/models.go
|
||||
package lsp
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
type LSP struct {
|
||||
Pubkey string
|
||||
Url string
|
||||
|
|
@ -70,3 +73,17 @@ func AlbyMutinynetPlebsLSP() LSP {
|
|||
}
|
||||
return lsp
|
||||
}
|
||||
|
||||
type LSPService interface {
|
||||
NewInstantChannelInvoice(ctx context.Context, request *NewInstantChannelInvoiceRequest) (*NewInstantChannelInvoiceResponse, error)
|
||||
}
|
||||
|
||||
type NewInstantChannelInvoiceRequest struct {
|
||||
Amount uint64 `json:"amount"`
|
||||
LSP string `json:"lsp"`
|
||||
}
|
||||
|
||||
type NewInstantChannelInvoiceResponse struct {
|
||||
Invoice string `json:"invoice"`
|
||||
Fee uint64 `json:"fee"`
|
||||
}
|
||||
19
main_http.go
19
main_http.go
|
|
@ -8,13 +8,14 @@ package main
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
nethttp "net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
echologrus "github.com/davrux/echo-logrus/v4"
|
||||
"github.com/getAlby/nostr-wallet-connect/http"
|
||||
"github.com/labstack/echo/v4"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
|
@ -26,26 +27,26 @@ func main() {
|
|||
ctx, _ := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM, os.Kill)
|
||||
svc, _ := NewService(ctx)
|
||||
|
||||
echologrus.Logger = svc.Logger
|
||||
echologrus.Logger = svc.logger
|
||||
e := echo.New()
|
||||
|
||||
//register shared routes
|
||||
httpSvc := NewHttpService(svc)
|
||||
httpSvc := http.NewHttpService(svc, svc.logger, svc.db, svc.eventPublisher)
|
||||
httpSvc.RegisterSharedRoutes(e)
|
||||
//start Echo server
|
||||
go func() {
|
||||
if err := e.Start(fmt.Sprintf(":%v", svc.cfg.Env.Port)); err != nil && err != http.ErrServerClosed {
|
||||
svc.Logger.Fatalf("shutting down the server: %v", err)
|
||||
if err := e.Start(fmt.Sprintf(":%v", svc.cfg.GetEnv().Port)); err != nil && err != nethttp.ErrServerClosed {
|
||||
svc.logger.Fatalf("shutting down the server: %v", err)
|
||||
}
|
||||
}()
|
||||
//handle graceful shutdown
|
||||
<-ctx.Done()
|
||||
svc.Logger.Infof("Shutting down echo server...")
|
||||
svc.logger.Infof("Shutting down echo server...")
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
e.Shutdown(ctx)
|
||||
svc.Logger.Info("Echo server exited")
|
||||
svc.Logger.Info("Waiting for service to exit...")
|
||||
svc.logger.Info("Echo server exited")
|
||||
svc.logger.Info("Waiting for service to exit...")
|
||||
svc.wg.Wait()
|
||||
svc.Logger.Info("Service exited")
|
||||
svc.logger.Info("Service exited")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,12 +18,12 @@ func main() {
|
|||
|
||||
app := NewApp(svc)
|
||||
LaunchWailsApp(app)
|
||||
svc.Logger.Info("Wails app exited")
|
||||
svc.logger.Info("Wails app exited")
|
||||
|
||||
svc.Logger.Info("Cancelling service context...")
|
||||
svc.logger.Info("Cancelling service context...")
|
||||
// cancel the service context
|
||||
cancel()
|
||||
svc.Logger.Info("Waiting for service to exit...")
|
||||
svc.logger.Info("Waiting for service to exit...")
|
||||
svc.wg.Wait()
|
||||
svc.Logger.Info("Service exited")
|
||||
svc.logger.Info("Service exited")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import (
|
|||
|
||||
"database/sql"
|
||||
|
||||
"github.com/getAlby/nostr-wallet-connect/models/config"
|
||||
"github.com/getAlby/nostr-wallet-connect/config"
|
||||
"github.com/go-gormigrate/gormigrate/v2"
|
||||
"github.com/sirupsen/logrus"
|
||||
"gorm.io/gorm"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package migrations
|
||||
|
||||
import (
|
||||
"github.com/getAlby/nostr-wallet-connect/models/config"
|
||||
"github.com/getAlby/nostr-wallet-connect/config"
|
||||
"github.com/go-gormigrate/gormigrate/v2"
|
||||
"github.com/sirupsen/logrus"
|
||||
"gorm.io/gorm"
|
||||
|
|
|
|||
204
models.go
204
models.go
|
|
@ -1,204 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/getAlby/nostr-wallet-connect/models/lnclient"
|
||||
)
|
||||
|
||||
const (
|
||||
REQUEST_EVENT_STATE_HANDLER_EXECUTING = "executing"
|
||||
REQUEST_EVENT_STATE_HANDLER_EXECUTED = "executed"
|
||||
REQUEST_EVENT_STATE_HANDLER_ERROR = "error"
|
||||
)
|
||||
const (
|
||||
RESPONSE_EVENT_STATE_PUBLISH_CONFIRMED = "confirmed"
|
||||
RESPONSE_EVENT_STATE_PUBLISH_FAILED = "failed"
|
||||
RESPONSE_EVENT_STATE_PUBLISH_UNCONFIRMED = "unconfirmed"
|
||||
)
|
||||
|
||||
// TODO: move to models/db
|
||||
type App struct {
|
||||
ID uint
|
||||
Name string `validate:"required"`
|
||||
Description string
|
||||
NostrPubkey string `validate:"required"`
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
// TODO: move to models/db
|
||||
type AppPermission struct {
|
||||
ID uint
|
||||
AppId uint `validate:"required"`
|
||||
App App
|
||||
RequestMethod string `validate:"required"`
|
||||
MaxAmount int
|
||||
BudgetRenewal string
|
||||
ExpiresAt *time.Time
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
// TODO: move to models/db
|
||||
type RequestEvent struct {
|
||||
ID uint
|
||||
AppId *uint
|
||||
App App
|
||||
NostrId string `validate:"required"`
|
||||
Content string
|
||||
State string
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
// TODO: move to models/db
|
||||
type ResponseEvent struct {
|
||||
ID uint
|
||||
NostrId string `validate:"required"`
|
||||
RequestId uint `validate:"required"`
|
||||
Content string
|
||||
State string
|
||||
RepliedAt time.Time
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
// TODO: move to models/db
|
||||
type Payment struct {
|
||||
ID uint
|
||||
AppId uint `validate:"required"`
|
||||
App App
|
||||
RequestEventId uint `validate:"required"`
|
||||
RequestEvent RequestEvent
|
||||
Amount uint // in sats
|
||||
PaymentRequest string
|
||||
Preimage *string
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
// TODO: move to models/Nip47
|
||||
type Nip47Transaction = lnclient.Transaction
|
||||
|
||||
type PayRequest struct {
|
||||
Invoice string `json:"invoice"`
|
||||
}
|
||||
|
||||
// TODO: move to models/Nip47
|
||||
type Nip47Request struct {
|
||||
Method string `json:"method"`
|
||||
Params json.RawMessage `json:"params"`
|
||||
}
|
||||
|
||||
type Nip47Response struct {
|
||||
Error *Nip47Error `json:"error,omitempty"`
|
||||
Result interface{} `json:"result,omitempty"`
|
||||
ResultType string `json:"result_type"`
|
||||
}
|
||||
|
||||
type Nip47Notification struct {
|
||||
Notification interface{} `json:"notification,omitempty"`
|
||||
NotificationType string `json:"notification_type"`
|
||||
}
|
||||
|
||||
type Nip47Error struct {
|
||||
Code string `json:"code,omitempty"`
|
||||
Message string `json:"message,omitempty"`
|
||||
}
|
||||
|
||||
type Nip47PaymentReceivedNotification struct {
|
||||
Nip47Transaction
|
||||
}
|
||||
|
||||
type Nip47PayParams struct {
|
||||
Invoice string `json:"invoice"`
|
||||
}
|
||||
type Nip47PayResponse struct {
|
||||
Preimage string `json:"preimage"`
|
||||
FeesPaid *uint64 `json:"fees_paid"`
|
||||
}
|
||||
|
||||
type Nip47MultiPayKeysendParams struct {
|
||||
Keysends []Nip47MultiPayKeysendElement `json:"keysends"`
|
||||
}
|
||||
|
||||
type Nip47MultiPayKeysendElement struct {
|
||||
Nip47KeysendParams
|
||||
Id string `json:"id"`
|
||||
}
|
||||
|
||||
type Nip47MultiPayInvoiceParams struct {
|
||||
Invoices []Nip47MultiPayInvoiceElement `json:"invoices"`
|
||||
}
|
||||
|
||||
type Nip47MultiPayInvoiceElement struct {
|
||||
Nip47PayParams
|
||||
Id string `json:"id"`
|
||||
}
|
||||
|
||||
type Nip47KeysendParams struct {
|
||||
Amount int64 `json:"amount"`
|
||||
Pubkey string `json:"pubkey"`
|
||||
Preimage string `json:"preimage"`
|
||||
TLVRecords []lnclient.TLVRecord `json:"tlv_records"`
|
||||
}
|
||||
|
||||
type Nip47BalanceResponse struct {
|
||||
Balance int64 `json:"balance"`
|
||||
MaxAmount int `json:"max_amount"`
|
||||
BudgetRenewal string `json:"budget_renewal"`
|
||||
}
|
||||
|
||||
// TODO: move to models/Nip47
|
||||
type Nip47GetInfoResponse struct {
|
||||
Alias string `json:"alias"`
|
||||
Color string `json:"color"`
|
||||
Pubkey string `json:"pubkey"`
|
||||
Network string `json:"network"`
|
||||
BlockHeight uint32 `json:"block_height"`
|
||||
BlockHash string `json:"block_hash"`
|
||||
Methods []string `json:"methods"`
|
||||
}
|
||||
|
||||
type Nip47MakeInvoiceParams struct {
|
||||
Amount int64 `json:"amount"`
|
||||
Description string `json:"description"`
|
||||
DescriptionHash string `json:"description_hash"`
|
||||
Expiry int64 `json:"expiry"`
|
||||
}
|
||||
type Nip47MakeInvoiceResponse struct {
|
||||
Nip47Transaction
|
||||
}
|
||||
|
||||
type Nip47LookupInvoiceParams struct {
|
||||
Invoice string `json:"invoice"`
|
||||
PaymentHash string `json:"payment_hash"`
|
||||
}
|
||||
|
||||
type Nip47LookupInvoiceResponse struct {
|
||||
Nip47Transaction
|
||||
}
|
||||
|
||||
type Nip47ListTransactionsParams struct {
|
||||
From uint64 `json:"from,omitempty"`
|
||||
Until uint64 `json:"until,omitempty"`
|
||||
Limit uint64 `json:"limit,omitempty"`
|
||||
Offset uint64 `json:"offset,omitempty"`
|
||||
Unpaid bool `json:"unpaid,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
}
|
||||
|
||||
type Nip47ListTransactionsResponse struct {
|
||||
Transactions []Nip47Transaction `json:"transactions"`
|
||||
}
|
||||
|
||||
type Nip47SignMessageParams struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type Nip47SignMessageResponse struct {
|
||||
Message string `json:"message"`
|
||||
Signature string `json:"signature"`
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
// TODO: move to db/models.go
|
||||
package db
|
||||
|
||||
import "time"
|
||||
|
||||
type UserConfig struct {
|
||||
ID uint
|
||||
Key string
|
||||
Value string
|
||||
Encrypted bool
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
127
nip47/models.go
127
nip47/models.go
|
|
@ -1,5 +1,11 @@
|
|||
package nip47
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/getAlby/nostr-wallet-connect/lnclient"
|
||||
)
|
||||
|
||||
const (
|
||||
INFO_EVENT_KIND = 13194
|
||||
REQUEST_KIND = 23194
|
||||
|
|
@ -44,3 +50,124 @@ const (
|
|||
BUDGET_RENEWAL_YEARLY = "yearly"
|
||||
BUDGET_RENEWAL_NEVER = "never"
|
||||
)
|
||||
|
||||
type Transaction = lnclient.Transaction
|
||||
|
||||
type PayRequest struct {
|
||||
Invoice string `json:"invoice"`
|
||||
}
|
||||
|
||||
type Request struct {
|
||||
Method string `json:"method"`
|
||||
Params json.RawMessage `json:"params"`
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
Error *Error `json:"error,omitempty"`
|
||||
Result interface{} `json:"result,omitempty"`
|
||||
ResultType string `json:"result_type"`
|
||||
}
|
||||
|
||||
type Notification struct {
|
||||
Notification interface{} `json:"notification,omitempty"`
|
||||
NotificationType string `json:"notification_type"`
|
||||
}
|
||||
|
||||
type Error struct {
|
||||
Code string `json:"code,omitempty"`
|
||||
Message string `json:"message,omitempty"`
|
||||
}
|
||||
|
||||
type PaymentReceivedNotification struct {
|
||||
Transaction
|
||||
}
|
||||
|
||||
type PayParams struct {
|
||||
Invoice string `json:"invoice"`
|
||||
}
|
||||
type PayResponse struct {
|
||||
Preimage string `json:"preimage"`
|
||||
FeesPaid *uint64 `json:"fees_paid"`
|
||||
}
|
||||
|
||||
type MultiPayKeysendParams struct {
|
||||
Keysends []MultiPayKeysendElement `json:"keysends"`
|
||||
}
|
||||
|
||||
type MultiPayKeysendElement struct {
|
||||
KeysendParams
|
||||
Id string `json:"id"`
|
||||
}
|
||||
|
||||
type MultiPayInvoiceParams struct {
|
||||
Invoices []MultiPayInvoiceElement `json:"invoices"`
|
||||
}
|
||||
|
||||
type MultiPayInvoiceElement struct {
|
||||
PayParams
|
||||
Id string `json:"id"`
|
||||
}
|
||||
|
||||
type KeysendParams struct {
|
||||
Amount int64 `json:"amount"`
|
||||
Pubkey string `json:"pubkey"`
|
||||
Preimage string `json:"preimage"`
|
||||
TLVRecords []lnclient.TLVRecord `json:"tlv_records"`
|
||||
}
|
||||
|
||||
type BalanceResponse struct {
|
||||
Balance int64 `json:"balance"`
|
||||
MaxAmount int `json:"max_amount"`
|
||||
BudgetRenewal string `json:"budget_renewal"`
|
||||
}
|
||||
|
||||
type GetInfoResponse struct {
|
||||
Alias string `json:"alias"`
|
||||
Color string `json:"color"`
|
||||
Pubkey string `json:"pubkey"`
|
||||
Network string `json:"network"`
|
||||
BlockHeight uint32 `json:"block_height"`
|
||||
BlockHash string `json:"block_hash"`
|
||||
Methods []string `json:"methods"`
|
||||
}
|
||||
|
||||
type MakeInvoiceParams struct {
|
||||
Amount int64 `json:"amount"`
|
||||
Description string `json:"description"`
|
||||
DescriptionHash string `json:"description_hash"`
|
||||
Expiry int64 `json:"expiry"`
|
||||
}
|
||||
type MakeInvoiceResponse struct {
|
||||
Transaction
|
||||
}
|
||||
|
||||
type LookupInvoiceParams struct {
|
||||
Invoice string `json:"invoice"`
|
||||
PaymentHash string `json:"payment_hash"`
|
||||
}
|
||||
|
||||
type LookupInvoiceResponse struct {
|
||||
Transaction
|
||||
}
|
||||
|
||||
type ListTransactionsParams struct {
|
||||
From uint64 `json:"from,omitempty"`
|
||||
Until uint64 `json:"until,omitempty"`
|
||||
Limit uint64 `json:"limit,omitempty"`
|
||||
Offset uint64 `json:"offset,omitempty"`
|
||||
Unpaid bool `json:"unpaid,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
}
|
||||
|
||||
type ListTransactionsResponse struct {
|
||||
Transactions []Transaction `json:"transactions"`
|
||||
}
|
||||
|
||||
type SignMessageParams struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type SignMessageResponse struct {
|
||||
Message string `json:"message"`
|
||||
Signature string `json:"signature"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
// TODO: move to nip47
|
||||
package main
|
||||
|
||||
import (
|
||||
|
|
@ -5,6 +6,7 @@ import (
|
|||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/getAlby/nostr-wallet-connect/db"
|
||||
"github.com/getAlby/nostr-wallet-connect/events"
|
||||
"github.com/getAlby/nostr-wallet-connect/nip47"
|
||||
"github.com/nbd-wtf/go-nostr"
|
||||
|
|
@ -39,28 +41,28 @@ func (notifier *Nip47Notifier) ConsumeEvent(ctx context.Context, event *events.E
|
|||
|
||||
paymentReceivedEventProperties, ok := event.Properties.(*events.PaymentReceivedEventProperties)
|
||||
if !ok {
|
||||
notifier.svc.Logger.WithField("event", event).Error("Failed to cast event")
|
||||
notifier.svc.logger.WithField("event", event).Error("Failed to cast event")
|
||||
return errors.New("failed to cast event")
|
||||
}
|
||||
|
||||
transaction, err := notifier.svc.lnClient.LookupInvoice(ctx, paymentReceivedEventProperties.PaymentHash)
|
||||
if err != nil {
|
||||
notifier.svc.Logger.
|
||||
notifier.svc.logger.
|
||||
WithField("paymentHash", paymentReceivedEventProperties.PaymentHash).
|
||||
WithError(err).
|
||||
Error("Failed to lookup invoice by payment hash")
|
||||
return err
|
||||
}
|
||||
|
||||
notifier.notifySubscribers(ctx, &Nip47Notification{
|
||||
notifier.notifySubscribers(ctx, &nip47.Notification{
|
||||
Notification: transaction,
|
||||
NotificationType: nip47.PAYMENT_RECEIVED_NOTIFICATION,
|
||||
}, nostr.Tags{})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (notifier *Nip47Notifier) notifySubscribers(ctx context.Context, notification *Nip47Notification, tags nostr.Tags) {
|
||||
apps := []App{}
|
||||
func (notifier *Nip47Notifier) notifySubscribers(ctx context.Context, notification *nip47.Notification, tags nostr.Tags) {
|
||||
apps := []db.App{}
|
||||
|
||||
// TODO: join apps and permissions
|
||||
notifier.svc.db.Find(&apps)
|
||||
|
|
@ -74,15 +76,15 @@ func (notifier *Nip47Notifier) notifySubscribers(ctx context.Context, notificati
|
|||
}
|
||||
}
|
||||
|
||||
func (notifier *Nip47Notifier) notifySubscriber(ctx context.Context, app *App, notification *Nip47Notification, tags nostr.Tags) {
|
||||
notifier.svc.Logger.WithFields(logrus.Fields{
|
||||
func (notifier *Nip47Notifier) notifySubscriber(ctx context.Context, app *db.App, notification *nip47.Notification, tags nostr.Tags) {
|
||||
notifier.svc.logger.WithFields(logrus.Fields{
|
||||
"notification": notification,
|
||||
"appId": app.ID,
|
||||
}).Info("Notifying subscriber")
|
||||
|
||||
ss, err := nip04.ComputeSharedSecret(app.NostrPubkey, notifier.svc.cfg.NostrSecretKey)
|
||||
ss, err := nip04.ComputeSharedSecret(app.NostrPubkey, notifier.svc.cfg.GetNostrSecretKey())
|
||||
if err != nil {
|
||||
notifier.svc.Logger.WithFields(logrus.Fields{
|
||||
notifier.svc.logger.WithFields(logrus.Fields{
|
||||
"notification": notification,
|
||||
"appId": app.ID,
|
||||
}).WithError(err).Error("Failed to compute shared secret")
|
||||
|
|
@ -91,7 +93,7 @@ func (notifier *Nip47Notifier) notifySubscriber(ctx context.Context, app *App, n
|
|||
|
||||
payloadBytes, err := json.Marshal(notification)
|
||||
if err != nil {
|
||||
notifier.svc.Logger.WithFields(logrus.Fields{
|
||||
notifier.svc.logger.WithFields(logrus.Fields{
|
||||
"notification": notification,
|
||||
"appId": app.ID,
|
||||
}).WithError(err).Error("Failed to stringify notification")
|
||||
|
|
@ -99,7 +101,7 @@ func (notifier *Nip47Notifier) notifySubscriber(ctx context.Context, app *App, n
|
|||
}
|
||||
msg, err := nip04.Encrypt(string(payloadBytes), ss)
|
||||
if err != nil {
|
||||
notifier.svc.Logger.WithFields(logrus.Fields{
|
||||
notifier.svc.logger.WithFields(logrus.Fields{
|
||||
"notification": notification,
|
||||
"appId": app.ID,
|
||||
}).WithError(err).Error("Failed to encrypt notification payload")
|
||||
|
|
@ -110,15 +112,15 @@ func (notifier *Nip47Notifier) notifySubscriber(ctx context.Context, app *App, n
|
|||
allTags = append(allTags, tags...)
|
||||
|
||||
event := &nostr.Event{
|
||||
PubKey: notifier.svc.cfg.NostrPublicKey,
|
||||
PubKey: notifier.svc.cfg.GetNostrPublicKey(),
|
||||
CreatedAt: nostr.Now(),
|
||||
Kind: nip47.NOTIFICATION_KIND,
|
||||
Tags: allTags,
|
||||
Content: msg,
|
||||
}
|
||||
err = event.Sign(notifier.svc.cfg.NostrSecretKey)
|
||||
err = event.Sign(notifier.svc.cfg.GetNostrSecretKey())
|
||||
if err != nil {
|
||||
notifier.svc.Logger.WithFields(logrus.Fields{
|
||||
notifier.svc.logger.WithFields(logrus.Fields{
|
||||
"notification": notification,
|
||||
"appId": app.ID,
|
||||
}).WithError(err).Error("Failed to sign event")
|
||||
|
|
@ -127,13 +129,13 @@ func (notifier *Nip47Notifier) notifySubscriber(ctx context.Context, app *App, n
|
|||
|
||||
err = notifier.relay.Publish(ctx, *event)
|
||||
if err != nil {
|
||||
notifier.svc.Logger.WithFields(logrus.Fields{
|
||||
notifier.svc.logger.WithFields(logrus.Fields{
|
||||
"notification": notification,
|
||||
"appId": app.ID,
|
||||
}).WithError(err).Error("Failed to publish notification")
|
||||
return
|
||||
}
|
||||
notifier.svc.Logger.WithFields(logrus.Fields{
|
||||
notifier.svc.logger.WithFields(logrus.Fields{
|
||||
"notification": notification,
|
||||
"appId": app.ID,
|
||||
}).Info("Published notification event")
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import (
|
|||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/getAlby/nostr-wallet-connect/db"
|
||||
"github.com/getAlby/nostr-wallet-connect/events"
|
||||
"github.com/getAlby/nostr-wallet-connect/nip47"
|
||||
"github.com/nbd-wtf/go-nostr"
|
||||
|
|
@ -24,7 +25,7 @@ func TestSendNotification(t *testing.T) {
|
|||
app, ss, err := createApp(svc)
|
||||
assert.NoError(t, err)
|
||||
|
||||
appPermission := &AppPermission{
|
||||
appPermission := &db.AppPermission{
|
||||
AppId: app.ID,
|
||||
App: *app,
|
||||
RequestMethod: nip47.NOTIFICATIONS_PERMISSION,
|
||||
|
|
@ -32,8 +33,8 @@ func TestSendNotification(t *testing.T) {
|
|||
err = svc.db.Create(appPermission).Error
|
||||
assert.NoError(t, err)
|
||||
|
||||
svc.nip47NotificationQueue = nip47.NewNip47NotificationQueue(svc.Logger)
|
||||
svc.EventPublisher.RegisterSubscriber(svc.nip47NotificationQueue)
|
||||
svc.nip47NotificationQueue = nip47.NewNip47NotificationQueue(svc.logger)
|
||||
svc.eventPublisher.RegisterSubscriber(svc.nip47NotificationQueue)
|
||||
|
||||
testEvent := &events.Event{
|
||||
Event: "nwc_payment_received",
|
||||
|
|
@ -44,7 +45,7 @@ func TestSendNotification(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
svc.EventPublisher.Publish(testEvent)
|
||||
svc.eventPublisher.Publish(testEvent)
|
||||
|
||||
receivedEvent := <-svc.nip47NotificationQueue.Channel()
|
||||
assert.Equal(t, testEvent, receivedEvent)
|
||||
|
|
@ -59,15 +60,15 @@ func TestSendNotification(t *testing.T) {
|
|||
|
||||
decrypted, err := nip04.Decrypt(relay.publishedEvent.Content, ss)
|
||||
assert.NoError(t, err)
|
||||
unmarshalledResponse := Nip47Notification{
|
||||
Notification: &Nip47PaymentReceivedNotification{},
|
||||
unmarshalledResponse := nip47.Notification{
|
||||
Notification: &nip47.PaymentReceivedNotification{},
|
||||
}
|
||||
|
||||
err = json.Unmarshal([]byte(decrypted), &unmarshalledResponse)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, nip47.PAYMENT_RECEIVED_NOTIFICATION, unmarshalledResponse.NotificationType)
|
||||
|
||||
transaction := (unmarshalledResponse.Notification.(*Nip47PaymentReceivedNotification))
|
||||
transaction := (unmarshalledResponse.Notification.(*nip47.PaymentReceivedNotification))
|
||||
assert.Equal(t, mockTransaction.Type, transaction.Type)
|
||||
assert.Equal(t, mockTransaction.Invoice, transaction.Invoice)
|
||||
assert.Equal(t, mockTransaction.Description, transaction.Description)
|
||||
|
|
@ -89,8 +90,8 @@ func TestSendNotificationNoPermission(t *testing.T) {
|
|||
_, _, err = createApp(svc)
|
||||
assert.NoError(t, err)
|
||||
|
||||
svc.nip47NotificationQueue = nip47.NewNip47NotificationQueue(svc.Logger)
|
||||
svc.EventPublisher.RegisterSubscriber(svc.nip47NotificationQueue)
|
||||
svc.nip47NotificationQueue = nip47.NewNip47NotificationQueue(svc.logger)
|
||||
svc.eventPublisher.RegisterSubscriber(svc.nip47NotificationQueue)
|
||||
|
||||
testEvent := &events.Event{
|
||||
Event: "nwc_payment_received",
|
||||
|
|
@ -101,7 +102,7 @@ func TestSendNotificationNoPermission(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
svc.EventPublisher.Publish(testEvent)
|
||||
svc.eventPublisher.Publish(testEvent)
|
||||
|
||||
receivedEvent := <-svc.nip47NotificationQueue.Channel()
|
||||
assert.Equal(t, testEvent, receivedEvent)
|
||||
|
|
|
|||
298
service.go
298
service.go
|
|
@ -28,10 +28,17 @@ import (
|
|||
|
||||
alby "github.com/getAlby/nostr-wallet-connect/alby"
|
||||
"github.com/getAlby/nostr-wallet-connect/events"
|
||||
"github.com/getAlby/nostr-wallet-connect/utils"
|
||||
|
||||
"github.com/getAlby/nostr-wallet-connect/config"
|
||||
"github.com/getAlby/nostr-wallet-connect/db"
|
||||
"github.com/getAlby/nostr-wallet-connect/lnclient"
|
||||
"github.com/getAlby/nostr-wallet-connect/lnclient/breez"
|
||||
"github.com/getAlby/nostr-wallet-connect/lnclient/greenlight"
|
||||
"github.com/getAlby/nostr-wallet-connect/lnclient/ldk"
|
||||
"github.com/getAlby/nostr-wallet-connect/lnclient/lnd"
|
||||
"github.com/getAlby/nostr-wallet-connect/lnclient/phoenixd"
|
||||
"github.com/getAlby/nostr-wallet-connect/migrations"
|
||||
"github.com/getAlby/nostr-wallet-connect/models/config"
|
||||
"github.com/getAlby/nostr-wallet-connect/models/lnclient"
|
||||
"github.com/getAlby/nostr-wallet-connect/nip47"
|
||||
)
|
||||
|
||||
|
|
@ -40,24 +47,25 @@ const (
|
|||
logFilename = "nwc.log"
|
||||
)
|
||||
|
||||
// TODO: move to service/
|
||||
// TODO: do not expose service struct
|
||||
type Service struct {
|
||||
// config from .env only. Fetch dynamic config from db
|
||||
cfg *Config
|
||||
// config from .GetEnv() only. Fetch dynamic config from db
|
||||
cfg config.Config
|
||||
db *gorm.DB
|
||||
lnClient lnclient.LNClient
|
||||
Logger *logrus.Logger
|
||||
AlbyOAuthSvc *alby.AlbyOAuthService
|
||||
EventPublisher events.EventPublisher
|
||||
logger *logrus.Logger
|
||||
albyOAuthSvc alby.AlbyOAuthService
|
||||
eventPublisher events.EventPublisher
|
||||
ctx context.Context
|
||||
wg *sync.WaitGroup
|
||||
nip47NotificationQueue nip47.Nip47NotificationQueue
|
||||
appCancelFn context.CancelFunc
|
||||
lastWalletSyncRequest time.Time
|
||||
}
|
||||
|
||||
// TODO: move to service.go
|
||||
func NewService(ctx context.Context) (*Service, error) {
|
||||
// Load config from environment variables / .env file
|
||||
// Load config from environment variables / .GetEnv() file
|
||||
godotenv.Load(".env")
|
||||
appConfig := &config.AppConfig{}
|
||||
err := envconfig.Process("", appConfig)
|
||||
|
|
@ -107,28 +115,27 @@ func NewService(ctx context.Context) (*Service, error) {
|
|||
}
|
||||
}
|
||||
|
||||
var db *gorm.DB
|
||||
var gormDB *gorm.DB
|
||||
var sqlDb *sql.DB
|
||||
db, err = gorm.Open(sqlite.Open(appConfig.DatabaseUri), &gorm.Config{})
|
||||
gormDB, err = gorm.Open(sqlite.Open(appConfig.DatabaseUri), &gorm.Config{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Enable foreign keys for sqlite
|
||||
db.Exec("PRAGMA foreign_keys=ON;")
|
||||
sqlDb, err = db.DB()
|
||||
gormDB.Exec("PRAGMA foreign_keys=ON;")
|
||||
sqlDb, err = gormDB.DB()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sqlDb.SetMaxOpenConns(1)
|
||||
|
||||
err = migrations.Migrate(db, appConfig, logger)
|
||||
err = migrations.Migrate(gormDB, appConfig, logger)
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Failed to migrate")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cfg := &Config{}
|
||||
cfg.Init(db, appConfig, logger)
|
||||
cfg := config.NewConfig(gormDB, appConfig, logger)
|
||||
|
||||
eventPublisher := events.NewEventPublisher(logger)
|
||||
|
||||
|
|
@ -143,17 +150,16 @@ func NewService(ctx context.Context) (*Service, error) {
|
|||
var wg sync.WaitGroup
|
||||
svc := &Service{
|
||||
cfg: cfg,
|
||||
db: db,
|
||||
db: gormDB,
|
||||
ctx: ctx,
|
||||
wg: &wg,
|
||||
Logger: logger,
|
||||
EventPublisher: eventPublisher,
|
||||
logger: logger,
|
||||
eventPublisher: eventPublisher,
|
||||
nip47NotificationQueue: nip47NotificationQueue,
|
||||
albyOAuthSvc: alby.NewAlbyOAuthService(logger, cfg, cfg.GetEnv(), db.NewDBService(gormDB, logger)),
|
||||
}
|
||||
// FIXME: tangled dependency
|
||||
svc.AlbyOAuthSvc = alby.NewAlbyOAuthService(logger, cfg, cfg.Env, NewAPI(svc))
|
||||
|
||||
eventPublisher.RegisterSubscriber(svc.AlbyOAuthSvc)
|
||||
eventPublisher.RegisterSubscriber(svc.albyOAuthSvc)
|
||||
|
||||
eventPublisher.Publish(&events.Event{
|
||||
Event: "nwc_started",
|
||||
|
|
@ -164,11 +170,11 @@ func NewService(ctx context.Context) (*Service, error) {
|
|||
|
||||
func (svc *Service) StopLNClient() error {
|
||||
if svc.lnClient != nil {
|
||||
svc.Logger.Info("Shutting down LDK client")
|
||||
svc.logger.Info("Shutting down LDK client")
|
||||
err := svc.lnClient.Shutdown()
|
||||
if err != nil {
|
||||
svc.Logger.WithError(err).Error("Failed to stop LN backend")
|
||||
svc.EventPublisher.Publish(&events.Event{
|
||||
svc.logger.WithError(err).Error("Failed to stop LN backend")
|
||||
svc.eventPublisher.Publish(&events.Event{
|
||||
Event: "nwc_node_stop_failed",
|
||||
Properties: map[string]interface{}{
|
||||
"error": fmt.Sprintf("%v", err),
|
||||
|
|
@ -176,13 +182,13 @@ func (svc *Service) StopLNClient() error {
|
|||
})
|
||||
return err
|
||||
}
|
||||
svc.Logger.Info("Publishing node shutdown event")
|
||||
svc.logger.Info("Publishing node shutdown event")
|
||||
svc.lnClient = nil
|
||||
svc.EventPublisher.Publish(&events.Event{
|
||||
svc.eventPublisher.Publish(&events.Event{
|
||||
Event: "nwc_node_stopped",
|
||||
})
|
||||
}
|
||||
svc.Logger.Info("LNClient stopped successfully")
|
||||
svc.logger.Info("LNClient stopped successfully")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -197,51 +203,51 @@ func (svc *Service) launchLNBackend(ctx context.Context, encryptionKey string) e
|
|||
return errors.New("no LNBackendType specified")
|
||||
}
|
||||
|
||||
svc.Logger.Infof("Launching LN Backend: %s", lnBackend)
|
||||
svc.logger.Infof("Launching LN Backend: %s", lnBackend)
|
||||
var lnClient lnclient.LNClient
|
||||
switch lnBackend {
|
||||
case config.LNDBackendType:
|
||||
LNDAddress, _ := svc.cfg.Get("LNDAddress", encryptionKey)
|
||||
LNDCertHex, _ := svc.cfg.Get("LNDCertHex", encryptionKey)
|
||||
LNDMacaroonHex, _ := svc.cfg.Get("LNDMacaroonHex", encryptionKey)
|
||||
lnClient, err = NewLNDService(ctx, svc, LNDAddress, LNDCertHex, LNDMacaroonHex)
|
||||
lnClient, err = lnd.NewLNDService(ctx, svc.logger, LNDAddress, LNDCertHex, LNDMacaroonHex)
|
||||
case config.LDKBackendType:
|
||||
Mnemonic, _ := svc.cfg.Get("Mnemonic", encryptionKey)
|
||||
LDKWorkdir := path.Join(svc.cfg.Env.Workdir, "ldk")
|
||||
LDKWorkdir := path.Join(svc.cfg.GetEnv().Workdir, "ldk")
|
||||
|
||||
lnClient, err = NewLDKService(ctx, svc, Mnemonic, LDKWorkdir, svc.cfg.Env.LDKNetwork, svc.cfg.Env.LDKEsploraServer, svc.cfg.Env.LDKGossipSource)
|
||||
lnClient, err = ldk.NewLDKService(ctx, svc.logger, svc.cfg, svc.eventPublisher, Mnemonic, LDKWorkdir, svc.cfg.GetEnv().LDKNetwork, svc.cfg.GetEnv().LDKEsploraServer, svc.cfg.GetEnv().LDKGossipSource)
|
||||
case config.GreenlightBackendType:
|
||||
Mnemonic, _ := svc.cfg.Get("Mnemonic", encryptionKey)
|
||||
GreenlightInviteCode, _ := svc.cfg.Get("GreenlightInviteCode", encryptionKey)
|
||||
GreenlightWorkdir := path.Join(svc.cfg.Env.Workdir, "greenlight")
|
||||
GreenlightWorkdir := path.Join(svc.cfg.GetEnv().Workdir, "greenlight")
|
||||
|
||||
lnClient, err = NewGreenlightService(svc, Mnemonic, GreenlightInviteCode, GreenlightWorkdir, encryptionKey)
|
||||
lnClient, err = greenlight.NewGreenlightService(svc.cfg, svc.logger, Mnemonic, GreenlightInviteCode, GreenlightWorkdir, encryptionKey)
|
||||
case config.BreezBackendType:
|
||||
Mnemonic, _ := svc.cfg.Get("Mnemonic", encryptionKey)
|
||||
BreezAPIKey, _ := svc.cfg.Get("BreezAPIKey", encryptionKey)
|
||||
GreenlightInviteCode, _ := svc.cfg.Get("GreenlightInviteCode", encryptionKey)
|
||||
BreezWorkdir := path.Join(svc.cfg.Env.Workdir, "breez")
|
||||
BreezWorkdir := path.Join(svc.cfg.GetEnv().Workdir, "breez")
|
||||
|
||||
lnClient, err = NewBreezService(svc.Logger, Mnemonic, BreezAPIKey, GreenlightInviteCode, BreezWorkdir)
|
||||
lnClient, err = breez.NewBreezService(svc.logger, Mnemonic, BreezAPIKey, GreenlightInviteCode, BreezWorkdir)
|
||||
case config.PhoenixBackendType:
|
||||
lnClient, err = NewPhoenixService(svc, svc.cfg.Env.PhoenixdAddress, svc.cfg.Env.PhoenixdAuthorization)
|
||||
lnClient, err = phoenixd.NewPhoenixService(svc.logger, svc.cfg.GetEnv().PhoenixdAddress, svc.cfg.GetEnv().PhoenixdAuthorization)
|
||||
default:
|
||||
svc.Logger.Fatalf("Unsupported LNBackendType: %v", lnBackend)
|
||||
svc.logger.Fatalf("Unsupported LNBackendType: %v", lnBackend)
|
||||
}
|
||||
if err != nil {
|
||||
svc.Logger.WithError(err).Error("Failed to launch LN backend")
|
||||
svc.logger.WithError(err).Error("Failed to launch LN backend")
|
||||
return err
|
||||
}
|
||||
|
||||
info, err := lnClient.GetInfo(ctx)
|
||||
if err != nil {
|
||||
svc.Logger.WithError(err).Error("Failed to fetch node info")
|
||||
svc.logger.WithError(err).Error("Failed to fetch node info")
|
||||
}
|
||||
if info != nil && info.Pubkey != "" {
|
||||
svc.EventPublisher.SetGlobalProperty("node_id", info.Pubkey)
|
||||
svc.eventPublisher.SetGlobalProperty("node_id", info.Pubkey)
|
||||
}
|
||||
|
||||
svc.EventPublisher.Publish(&events.Event{
|
||||
svc.eventPublisher.Publish(&events.Event{
|
||||
Event: "nwc_node_started",
|
||||
Properties: map[string]interface{}{
|
||||
"node_type": lnBackend,
|
||||
|
|
@ -260,7 +266,11 @@ func (svc *Service) createFilters(identityPubkey string) nostr.Filters {
|
|||
}
|
||||
|
||||
func (svc *Service) noticeHandler(notice string) {
|
||||
svc.Logger.Infof("Received a notice %s", notice)
|
||||
svc.logger.Infof("Received a notice %s", notice)
|
||||
}
|
||||
|
||||
func (svc *Service) GetLNClient() lnclient.LNClient {
|
||||
return svc.lnClient
|
||||
}
|
||||
|
||||
func (svc *Service) StartSubscription(ctx context.Context, sub *nostr.Subscription) error {
|
||||
|
|
@ -280,34 +290,34 @@ func (svc *Service) StartSubscription(ctx context.Context, sub *nostr.Subscripti
|
|||
go func() {
|
||||
// block till EOS is received
|
||||
<-sub.EndOfStoredEvents
|
||||
svc.Logger.Info("Received EOS")
|
||||
svc.logger.Info("Received EOS")
|
||||
|
||||
// loop through incoming events
|
||||
for event := range sub.Events {
|
||||
go svc.HandleEvent(ctx, sub, event)
|
||||
}
|
||||
svc.Logger.Info("Relay subscription events channel ended")
|
||||
svc.logger.Info("Relay subscription events channel ended")
|
||||
}()
|
||||
|
||||
<-ctx.Done()
|
||||
|
||||
if sub.Relay.ConnectionError != nil {
|
||||
svc.Logger.WithField("connectionError", sub.Relay.ConnectionError).Error("Relay error")
|
||||
svc.logger.WithField("connectionError", sub.Relay.ConnectionError).Error("Relay error")
|
||||
return sub.Relay.ConnectionError
|
||||
}
|
||||
svc.Logger.Info("Exiting subscription...")
|
||||
svc.logger.Info("Exiting subscription...")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (svc *Service) PublishEvent(ctx context.Context, sub *nostr.Subscription, requestEvent *RequestEvent, resp *nostr.Event, app *App) error {
|
||||
func (svc *Service) PublishEvent(ctx context.Context, sub *nostr.Subscription, requestEvent *db.RequestEvent, resp *nostr.Event, app *db.App) error {
|
||||
var appId *uint
|
||||
if app != nil {
|
||||
appId = &app.ID
|
||||
}
|
||||
responseEvent := ResponseEvent{NostrId: resp.ID, RequestId: requestEvent.ID, Content: resp.Content, State: "received"}
|
||||
responseEvent := db.ResponseEvent{NostrId: resp.ID, RequestId: requestEvent.ID, Content: resp.Content, State: "received"}
|
||||
err := svc.db.Create(&responseEvent).Error
|
||||
if err != nil {
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
"requestEventNostrId": requestEvent.NostrId,
|
||||
"appId": appId,
|
||||
"replyEventId": resp.ID,
|
||||
|
|
@ -317,8 +327,8 @@ func (svc *Service) PublishEvent(ctx context.Context, sub *nostr.Subscription, r
|
|||
|
||||
err = sub.Relay.Publish(ctx, *resp)
|
||||
if err != nil {
|
||||
responseEvent.State = RESPONSE_EVENT_STATE_PUBLISH_FAILED
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
responseEvent.State = db.RESPONSE_EVENT_STATE_PUBLISH_FAILED
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
"requestEventId": requestEvent.ID,
|
||||
"requestNostrEventId": requestEvent.NostrId,
|
||||
"appId": appId,
|
||||
|
|
@ -326,9 +336,9 @@ func (svc *Service) PublishEvent(ctx context.Context, sub *nostr.Subscription, r
|
|||
"responseNostrEventId": resp.ID,
|
||||
}).Errorf("Failed to publish reply: %v", err)
|
||||
} else {
|
||||
responseEvent.State = RESPONSE_EVENT_STATE_PUBLISH_CONFIRMED
|
||||
responseEvent.State = db.RESPONSE_EVENT_STATE_PUBLISH_CONFIRMED
|
||||
responseEvent.RepliedAt = time.Now()
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
"requestEventId": requestEvent.ID,
|
||||
"requestNostrEventId": requestEvent.NostrId,
|
||||
"appId": appId,
|
||||
|
|
@ -339,7 +349,7 @@ func (svc *Service) PublishEvent(ctx context.Context, sub *nostr.Subscription, r
|
|||
|
||||
err = svc.db.Save(&responseEvent).Error
|
||||
if err != nil {
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
"requestEventId": requestEvent.ID,
|
||||
"requestNostrEventId": requestEvent.NostrId,
|
||||
"appId": appId,
|
||||
|
|
@ -353,25 +363,15 @@ func (svc *Service) PublishEvent(ctx context.Context, sub *nostr.Subscription, r
|
|||
}
|
||||
|
||||
func (svc *Service) HandleEvent(ctx context.Context, sub *nostr.Subscription, event *nostr.Event) {
|
||||
var nip47Response *Nip47Response
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
var nip47Response *nip47.Response
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
"requestEventNostrId": event.ID,
|
||||
"eventKind": event.Kind,
|
||||
}).Info("Processing Event")
|
||||
|
||||
// make sure we don't know the event, yet
|
||||
requestEvent := RequestEvent{}
|
||||
findEventResult := svc.db.Where("nostr_id = ?", event.ID).Find(&requestEvent)
|
||||
if findEventResult.RowsAffected != 0 {
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
"requestEventNostrId": event.ID,
|
||||
}).Warn("Event already processed")
|
||||
return
|
||||
}
|
||||
|
||||
ss, err := nip04.ComputeSharedSecret(event.PubKey, svc.cfg.NostrSecretKey)
|
||||
ss, err := nip04.ComputeSharedSecret(event.PubKey, svc.cfg.GetNostrSecretKey())
|
||||
if err != nil {
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
"requestEventNostrId": event.ID,
|
||||
"eventKind": event.Kind,
|
||||
}).Errorf("Failed to compute shared secret: %v", err)
|
||||
|
|
@ -379,22 +379,28 @@ func (svc *Service) HandleEvent(ctx context.Context, sub *nostr.Subscription, ev
|
|||
}
|
||||
|
||||
// store request event
|
||||
requestEvent = RequestEvent{AppId: nil, NostrId: event.ID, Content: event.Content, State: REQUEST_EVENT_STATE_HANDLER_EXECUTING}
|
||||
requestEvent := db.RequestEvent{AppId: nil, NostrId: event.ID, Content: event.Content, State: db.REQUEST_EVENT_STATE_HANDLER_EXECUTING}
|
||||
err = svc.db.Create(&requestEvent).Error
|
||||
if err != nil {
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
if errors.Is(err, gorm.ErrDuplicatedKey) {
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
"requestEventNostrId": event.ID,
|
||||
}).Warn("Event already processed")
|
||||
return
|
||||
}
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
"requestEventNostrId": event.ID,
|
||||
"eventKind": event.Kind,
|
||||
}).Errorf("Failed to save nostr event: %v", err)
|
||||
nip47Response = &Nip47Response{
|
||||
Error: &Nip47Error{
|
||||
nip47Response = &nip47.Response{
|
||||
Error: &nip47.Error{
|
||||
Code: nip47.ERROR_INTERNAL,
|
||||
Message: fmt.Sprintf("Failed to save nostr event: %s", err.Error()),
|
||||
},
|
||||
}
|
||||
resp, err := svc.createResponse(event, nip47Response, nostr.Tags{}, ss)
|
||||
if err != nil {
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
"requestEventNostrId": event.ID,
|
||||
"eventKind": event.Kind,
|
||||
}).Errorf("Failed to process event: %v", err)
|
||||
|
|
@ -403,34 +409,34 @@ func (svc *Service) HandleEvent(ctx context.Context, sub *nostr.Subscription, ev
|
|||
return
|
||||
}
|
||||
|
||||
app := App{}
|
||||
err = svc.db.First(&app, &App{
|
||||
app := db.App{}
|
||||
err = svc.db.First(&app, &db.App{
|
||||
NostrPubkey: event.PubKey,
|
||||
}).Error
|
||||
if err != nil {
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
"nostrPubkey": event.PubKey,
|
||||
}).Errorf("Failed to find app for nostr pubkey: %v", err)
|
||||
|
||||
nip47Response = &Nip47Response{
|
||||
Error: &Nip47Error{
|
||||
nip47Response = &nip47.Response{
|
||||
Error: &nip47.Error{
|
||||
Code: nip47.ERROR_UNAUTHORIZED,
|
||||
Message: "The public key does not have a wallet connected.",
|
||||
},
|
||||
}
|
||||
resp, err := svc.createResponse(event, nip47Response, nostr.Tags{}, ss)
|
||||
if err != nil {
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
"requestEventNostrId": event.ID,
|
||||
"eventKind": event.Kind,
|
||||
}).Errorf("Failed to process event: %v", err)
|
||||
}
|
||||
svc.PublishEvent(ctx, sub, &requestEvent, resp, &app)
|
||||
|
||||
requestEvent.State = REQUEST_EVENT_STATE_HANDLER_ERROR
|
||||
requestEvent.State = db.REQUEST_EVENT_STATE_HANDLER_ERROR
|
||||
err = svc.db.Save(&requestEvent).Error
|
||||
if err != nil {
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
"nostrPubkey": event.PubKey,
|
||||
}).Errorf("Failed to save state to nostr event: %v", err)
|
||||
}
|
||||
|
|
@ -440,29 +446,29 @@ func (svc *Service) HandleEvent(ctx context.Context, sub *nostr.Subscription, ev
|
|||
requestEvent.AppId = &app.ID
|
||||
err = svc.db.Save(&requestEvent).Error
|
||||
if err != nil {
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
"nostrPubkey": event.PubKey,
|
||||
}).Errorf("Failed to save app to nostr event: %v", err)
|
||||
|
||||
nip47Response = &Nip47Response{
|
||||
Error: &Nip47Error{
|
||||
nip47Response = &nip47.Response{
|
||||
Error: &nip47.Error{
|
||||
Code: nip47.ERROR_UNAUTHORIZED,
|
||||
Message: fmt.Sprintf("Failed to save app to nostr event: %s", err.Error()),
|
||||
},
|
||||
}
|
||||
resp, err := svc.createResponse(event, nip47Response, nostr.Tags{}, ss)
|
||||
if err != nil {
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
"requestEventNostrId": event.ID,
|
||||
"eventKind": event.Kind,
|
||||
}).Errorf("Failed to process event: %v", err)
|
||||
}
|
||||
svc.PublishEvent(ctx, sub, &requestEvent, resp, &app)
|
||||
|
||||
requestEvent.State = REQUEST_EVENT_STATE_HANDLER_ERROR
|
||||
requestEvent.State = db.REQUEST_EVENT_STATE_HANDLER_ERROR
|
||||
err = svc.db.Save(&requestEvent).Error
|
||||
if err != nil {
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
"nostrPubkey": event.PubKey,
|
||||
}).Errorf("Failed to save state to nostr event: %v", err)
|
||||
}
|
||||
|
|
@ -470,24 +476,24 @@ func (svc *Service) HandleEvent(ctx context.Context, sub *nostr.Subscription, ev
|
|||
return
|
||||
}
|
||||
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
"requestEventNostrId": event.ID,
|
||||
"eventKind": event.Kind,
|
||||
"appId": app.ID,
|
||||
}).Info("App found for nostr event")
|
||||
|
||||
//to be extra safe, decrypt using the key found from the app
|
||||
ss, err = nip04.ComputeSharedSecret(app.NostrPubkey, svc.cfg.NostrSecretKey)
|
||||
ss, err = nip04.ComputeSharedSecret(app.NostrPubkey, svc.cfg.GetNostrSecretKey())
|
||||
if err != nil {
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
"requestEventNostrId": event.ID,
|
||||
"eventKind": event.Kind,
|
||||
}).Errorf("Failed to process event: %v", err)
|
||||
|
||||
requestEvent.State = REQUEST_EVENT_STATE_HANDLER_ERROR
|
||||
requestEvent.State = db.REQUEST_EVENT_STATE_HANDLER_ERROR
|
||||
err = svc.db.Save(&requestEvent).Error
|
||||
if err != nil {
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
"nostrPubkey": event.PubKey,
|
||||
}).Errorf("Failed to save state to nostr event: %v", err)
|
||||
}
|
||||
|
|
@ -496,38 +502,38 @@ func (svc *Service) HandleEvent(ctx context.Context, sub *nostr.Subscription, ev
|
|||
}
|
||||
payload, err := nip04.Decrypt(event.Content, ss)
|
||||
if err != nil {
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
"requestEventNostrId": event.ID,
|
||||
"eventKind": event.Kind,
|
||||
"appId": app.ID,
|
||||
}).Errorf("Failed to decrypt content: %v", err)
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
"requestEventNostrId": event.ID,
|
||||
"eventKind": event.Kind,
|
||||
}).Errorf("Failed to process event: %v", err)
|
||||
|
||||
requestEvent.State = REQUEST_EVENT_STATE_HANDLER_ERROR
|
||||
requestEvent.State = db.REQUEST_EVENT_STATE_HANDLER_ERROR
|
||||
err = svc.db.Save(&requestEvent).Error
|
||||
if err != nil {
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
"nostrPubkey": event.PubKey,
|
||||
}).Errorf("Failed to save state to nostr event: %v", err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
nip47Request := &Nip47Request{}
|
||||
nip47Request := &nip47.Request{}
|
||||
err = json.Unmarshal([]byte(payload), nip47Request)
|
||||
if err != nil {
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
"requestEventNostrId": event.ID,
|
||||
"eventKind": event.Kind,
|
||||
}).Errorf("Failed to process event: %v", err)
|
||||
|
||||
requestEvent.State = REQUEST_EVENT_STATE_HANDLER_ERROR
|
||||
requestEvent.State = db.REQUEST_EVENT_STATE_HANDLER_ERROR
|
||||
err = svc.db.Save(&requestEvent).Error
|
||||
if err != nil {
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
"nostrPubkey": event.PubKey,
|
||||
}).Errorf("Failed to save state to nostr event: %v", err)
|
||||
}
|
||||
|
|
@ -537,29 +543,29 @@ func (svc *Service) HandleEvent(ctx context.Context, sub *nostr.Subscription, ev
|
|||
|
||||
// TODO: replace with a channel
|
||||
// TODO: update all previous occurences of svc.PublishEvent to also use the channel
|
||||
publishResponse := func(nip47Response *Nip47Response, tags nostr.Tags) {
|
||||
publishResponse := func(nip47Response *nip47.Response, tags nostr.Tags) {
|
||||
resp, err := svc.createResponse(event, nip47Response, tags, ss)
|
||||
if err != nil {
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
"requestEventNostrId": event.ID,
|
||||
"eventKind": event.Kind,
|
||||
}).Errorf("Failed to create response: %v", err)
|
||||
requestEvent.State = REQUEST_EVENT_STATE_HANDLER_ERROR
|
||||
requestEvent.State = db.REQUEST_EVENT_STATE_HANDLER_ERROR
|
||||
} else {
|
||||
err = svc.PublishEvent(ctx, sub, &requestEvent, resp, &app)
|
||||
if err != nil {
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
"requestEventNostrId": event.ID,
|
||||
"eventKind": event.Kind,
|
||||
}).Errorf("Failed to publish event: %v", err)
|
||||
requestEvent.State = REQUEST_EVENT_STATE_HANDLER_ERROR
|
||||
requestEvent.State = db.REQUEST_EVENT_STATE_HANDLER_ERROR
|
||||
} else {
|
||||
requestEvent.State = REQUEST_EVENT_STATE_HANDLER_EXECUTED
|
||||
requestEvent.State = db.REQUEST_EVENT_STATE_HANDLER_EXECUTED
|
||||
}
|
||||
}
|
||||
err = svc.db.Save(&requestEvent).Error
|
||||
if err != nil {
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
"nostrPubkey": event.PubKey,
|
||||
}).Errorf("Failed to save state to nostr event: %v", err)
|
||||
}
|
||||
|
|
@ -591,10 +597,10 @@ func (svc *Service) HandleEvent(ctx context.Context, sub *nostr.Subscription, ev
|
|||
}
|
||||
}
|
||||
|
||||
func (svc *Service) handleUnknownMethod(ctx context.Context, nip47Request *Nip47Request, publishResponse func(*Nip47Response, nostr.Tags)) {
|
||||
publishResponse(&Nip47Response{
|
||||
func (svc *Service) handleUnknownMethod(ctx context.Context, nip47Request *nip47.Request, publishResponse func(*nip47.Response, nostr.Tags)) {
|
||||
publishResponse(&nip47.Response{
|
||||
ResultType: nip47Request.Method,
|
||||
Error: &Nip47Error{
|
||||
Error: &nip47.Error{
|
||||
Code: nip47.ERROR_NOT_IMPLEMENTED,
|
||||
Message: fmt.Sprintf("Unknown method: %s", nip47Request.Method),
|
||||
},
|
||||
|
|
@ -615,22 +621,22 @@ func (svc *Service) createResponse(initialEvent *nostr.Event, content interface{
|
|||
allTags = append(allTags, tags...)
|
||||
|
||||
resp := &nostr.Event{
|
||||
PubKey: svc.cfg.NostrPublicKey,
|
||||
PubKey: svc.cfg.GetNostrPublicKey(),
|
||||
CreatedAt: nostr.Now(),
|
||||
Kind: nip47.RESPONSE_KIND,
|
||||
Tags: allTags,
|
||||
Content: msg,
|
||||
}
|
||||
err = resp.Sign(svc.cfg.NostrSecretKey)
|
||||
err = resp.Sign(svc.cfg.GetNostrSecretKey())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (svc *Service) GetMethods(app *App) []string {
|
||||
appPermissions := []AppPermission{}
|
||||
svc.db.Find(&appPermissions, &AppPermission{
|
||||
func (svc *Service) GetMethods(app *db.App) []string {
|
||||
appPermissions := []db.AppPermission{}
|
||||
svc.db.Find(&appPermissions, &db.AppPermission{
|
||||
AppId: app.ID,
|
||||
})
|
||||
requestMethods := make([]string, 0, len(appPermissions))
|
||||
|
|
@ -645,16 +651,16 @@ func (svc *Service) GetMethods(app *App) []string {
|
|||
return requestMethods
|
||||
}
|
||||
|
||||
func (svc *Service) decodeNip47Request(nip47Request *Nip47Request, requestEvent *RequestEvent, app *App, methodParams interface{}) *Nip47Response {
|
||||
func (svc *Service) decodeNip47Request(nip47Request *nip47.Request, requestEvent *db.RequestEvent, app *db.App, methodParams interface{}) *nip47.Response {
|
||||
err := json.Unmarshal(nip47Request.Params, methodParams)
|
||||
if err != nil {
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
"requestEventNostrId": requestEvent.NostrId,
|
||||
"appId": app.ID,
|
||||
}).Errorf("Failed to decode nostr event: %v", err)
|
||||
return &Nip47Response{
|
||||
return &nip47.Response{
|
||||
ResultType: nip47Request.Method,
|
||||
Error: &Nip47Error{
|
||||
Error: &nip47.Error{
|
||||
Code: nip47.ERROR_BAD_REQUEST,
|
||||
Message: err.Error(),
|
||||
}}
|
||||
|
|
@ -662,17 +668,17 @@ func (svc *Service) decodeNip47Request(nip47Request *Nip47Request, requestEvent
|
|||
return nil
|
||||
}
|
||||
|
||||
func (svc *Service) checkPermission(nip47Request *Nip47Request, requestNostrEventId string, app *App, amount int64) *Nip47Response {
|
||||
func (svc *Service) checkPermission(nip47Request *nip47.Request, requestNostrEventId string, app *db.App, amount int64) *nip47.Response {
|
||||
hasPermission, code, message := svc.hasPermission(app, nip47Request.Method, amount)
|
||||
if !hasPermission {
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
"requestEventNostrId": requestNostrEventId,
|
||||
"appId": app.ID,
|
||||
"code": code,
|
||||
"message": message,
|
||||
}).Error("App does not have permission")
|
||||
|
||||
svc.EventPublisher.Publish(&events.Event{
|
||||
svc.eventPublisher.Publish(&events.Event{
|
||||
Event: "nwc_permission_denied",
|
||||
Properties: map[string]interface{}{
|
||||
"request_method": nip47Request.Method,
|
||||
|
|
@ -683,9 +689,9 @@ func (svc *Service) checkPermission(nip47Request *Nip47Request, requestNostrEven
|
|||
},
|
||||
})
|
||||
|
||||
return &Nip47Response{
|
||||
return &nip47.Response{
|
||||
ResultType: nip47Request.Method,
|
||||
Error: &Nip47Error{
|
||||
Error: &nip47.Error{
|
||||
Code: code,
|
||||
Message: message,
|
||||
},
|
||||
|
|
@ -694,14 +700,14 @@ func (svc *Service) checkPermission(nip47Request *Nip47Request, requestNostrEven
|
|||
return nil
|
||||
}
|
||||
|
||||
func (svc *Service) hasPermission(app *App, requestMethod string, amount int64) (result bool, code string, message string) {
|
||||
func (svc *Service) hasPermission(app *db.App, requestMethod string, amount int64) (result bool, code string, message string) {
|
||||
switch requestMethod {
|
||||
case nip47.PAY_INVOICE_METHOD, nip47.PAY_KEYSEND_METHOD, nip47.MULTI_PAY_INVOICE_METHOD, nip47.MULTI_PAY_KEYSEND_METHOD:
|
||||
requestMethod = nip47.PAY_INVOICE_METHOD
|
||||
}
|
||||
|
||||
appPermission := AppPermission{}
|
||||
findPermissionResult := svc.db.Find(&appPermission, &AppPermission{
|
||||
appPermission := db.AppPermission{}
|
||||
findPermissionResult := svc.db.Find(&appPermission, &db.AppPermission{
|
||||
AppId: app.ID,
|
||||
RequestMethod: requestMethod,
|
||||
})
|
||||
|
|
@ -711,7 +717,7 @@ func (svc *Service) hasPermission(app *App, requestMethod string, amount int64)
|
|||
}
|
||||
expiresAt := appPermission.ExpiresAt
|
||||
if expiresAt != nil && expiresAt.Before(time.Now()) {
|
||||
svc.Logger.WithFields(logrus.Fields{
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
"requestMethod": requestMethod,
|
||||
"expiresAt": expiresAt.Unix(),
|
||||
"appId": app.ID,
|
||||
|
|
@ -734,12 +740,13 @@ func (svc *Service) hasPermission(app *App, requestMethod string, amount int64)
|
|||
return true, "", ""
|
||||
}
|
||||
|
||||
func (svc *Service) GetBudgetUsage(appPermission *AppPermission) int64 {
|
||||
// TODO: move somewhere else
|
||||
func (svc *Service) GetBudgetUsage(appPermission *db.AppPermission) int64 {
|
||||
var result struct {
|
||||
Sum uint
|
||||
}
|
||||
// TODO: discard failed payments from this check instead of checking payments that have a preimage
|
||||
svc.db.Table("payments").Select("SUM(amount) as sum").Where("app_id = ? AND preimage IS NOT NULL AND created_at > ?", appPermission.AppId, GetStartOfBudget(appPermission.BudgetRenewal, appPermission.App.CreatedAt)).Scan(&result)
|
||||
svc.db.Table("payments").Select("SUM(amount) as sum").Where("app_id = ? AND preimage IS NOT NULL AND created_at > ?", appPermission.AppId, utils.GetStartOfBudget(appPermission.BudgetRenewal, appPermission.App.CreatedAt)).Scan(&result)
|
||||
return int64(result.Sum)
|
||||
}
|
||||
|
||||
|
|
@ -748,9 +755,9 @@ func (svc *Service) PublishNip47Info(ctx context.Context, relay *nostr.Relay) er
|
|||
ev.Kind = nip47.INFO_EVENT_KIND
|
||||
ev.Content = nip47.CAPABILITIES
|
||||
ev.CreatedAt = nostr.Now()
|
||||
ev.PubKey = svc.cfg.NostrPublicKey
|
||||
ev.PubKey = svc.cfg.GetNostrPublicKey()
|
||||
ev.Tags = nostr.Tags{[]string{"notifications", nip47.NOTIFICATION_TYPES}}
|
||||
err := ev.Sign(svc.cfg.NostrSecretKey)
|
||||
err := ev.Sign(svc.cfg.GetNostrSecretKey())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -761,8 +768,8 @@ func (svc *Service) PublishNip47Info(ctx context.Context, relay *nostr.Relay) er
|
|||
return nil
|
||||
}
|
||||
|
||||
func (svc *Service) LogFilePath() string {
|
||||
return filepath.Join(svc.cfg.Env.Workdir, logDir, logFilename)
|
||||
func (svc *Service) GetLogFilePath() string {
|
||||
return filepath.Join(svc.cfg.GetEnv().Workdir, logDir, logFilename)
|
||||
}
|
||||
|
||||
func finishRestoreNode(logger *logrus.Logger, workDir string) {
|
||||
|
|
@ -803,3 +810,24 @@ func finishRestoreNode(logger *logrus.Logger, workDir string) {
|
|||
logger.WithField("restoreDir", restoreDir).Info("removed restore directory")
|
||||
}
|
||||
}
|
||||
|
||||
func (svc *Service) StopDb() error {
|
||||
db, err := svc.db.DB()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get database connection: %w", err)
|
||||
}
|
||||
|
||||
err = db.Close()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to close database connection: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (svc *Service) GetConfig() config.Config {
|
||||
return svc.cfg
|
||||
}
|
||||
|
||||
func (svc *Service) GetAlbyOAuthSvc() alby.AlbyOAuthService {
|
||||
return svc.albyOAuthSvc
|
||||
}
|
||||
|
|
|
|||
20
service/models.go
Normal file
20
service/models.go
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"github.com/getAlby/nostr-wallet-connect/alby"
|
||||
"github.com/getAlby/nostr-wallet-connect/config"
|
||||
"github.com/getAlby/nostr-wallet-connect/db"
|
||||
"github.com/getAlby/nostr-wallet-connect/lnclient"
|
||||
)
|
||||
|
||||
type Service interface {
|
||||
GetLNClient() lnclient.LNClient
|
||||
GetConfig() config.Config
|
||||
StartApp(encryptionKey string) error
|
||||
StopApp()
|
||||
StopLNClient() error
|
||||
StopDb() error
|
||||
GetBudgetUsage(appPermission *db.AppPermission) int64
|
||||
GetLogFilePath() string
|
||||
GetAlbyOAuthSvc() alby.AlbyOAuthService
|
||||
}
|
||||
247
service_test.go
247
service_test.go
|
|
@ -14,10 +14,11 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/getAlby/nostr-wallet-connect/config"
|
||||
"github.com/getAlby/nostr-wallet-connect/db"
|
||||
"github.com/getAlby/nostr-wallet-connect/events"
|
||||
"github.com/getAlby/nostr-wallet-connect/lnclient"
|
||||
"github.com/getAlby/nostr-wallet-connect/migrations"
|
||||
"github.com/getAlby/nostr-wallet-connect/models/config"
|
||||
"github.com/getAlby/nostr-wallet-connect/models/lnclient"
|
||||
"github.com/getAlby/nostr-wallet-connect/nip47"
|
||||
)
|
||||
|
||||
|
|
@ -223,7 +224,7 @@ var mockNodeInfo = lnclient.NodeInfo{
|
|||
var mockTime = time.Unix(1693876963, 0)
|
||||
var mockTimeUnix = mockTime.Unix()
|
||||
|
||||
var mockTransactions = []Nip47Transaction{
|
||||
var mockTransactions = []nip47.Transaction{
|
||||
{
|
||||
Type: "incoming",
|
||||
Invoice: mockInvoice,
|
||||
|
|
@ -287,7 +288,7 @@ func TestHasPermission_Expired(t *testing.T) {
|
|||
|
||||
budgetRenewal := "never"
|
||||
expiresAt := time.Now().Add(-24 * time.Hour)
|
||||
appPermission := &AppPermission{
|
||||
appPermission := &db.AppPermission{
|
||||
AppId: app.ID,
|
||||
App: *app,
|
||||
RequestMethod: nip47.PAY_INVOICE_METHOD,
|
||||
|
|
@ -316,7 +317,7 @@ func TestHasPermission_Exceeded(t *testing.T) {
|
|||
|
||||
budgetRenewal := "never"
|
||||
expiresAt := time.Now().Add(24 * time.Hour)
|
||||
appPermission := &AppPermission{
|
||||
appPermission := &db.AppPermission{
|
||||
AppId: app.ID,
|
||||
App: *app,
|
||||
RequestMethod: nip47.PAY_INVOICE_METHOD,
|
||||
|
|
@ -345,7 +346,7 @@ func TestHasPermission_OK(t *testing.T) {
|
|||
|
||||
budgetRenewal := "never"
|
||||
expiresAt := time.Now().Add(24 * time.Hour)
|
||||
appPermission := &AppPermission{
|
||||
appPermission := &db.AppPermission{
|
||||
AppId: app.ID,
|
||||
App: *app,
|
||||
RequestMethod: nip47.PAY_INVOICE_METHOD,
|
||||
|
|
@ -381,12 +382,12 @@ func TestCreateResponse(t *testing.T) {
|
|||
|
||||
reqEvent.ID = "12345"
|
||||
|
||||
ss, err := nip04.ComputeSharedSecret(reqPubkey, svc.cfg.NostrSecretKey)
|
||||
ss, err := nip04.ComputeSharedSecret(reqPubkey, svc.cfg.GetNostrSecretKey())
|
||||
assert.NoError(t, err)
|
||||
|
||||
nip47Response := &Nip47Response{
|
||||
nip47Response := &nip47.Response{
|
||||
ResultType: nip47.GET_BALANCE_METHOD,
|
||||
Result: Nip47BalanceResponse{
|
||||
Result: nip47.BalanceResponse{
|
||||
Balance: 1000,
|
||||
},
|
||||
}
|
||||
|
|
@ -394,18 +395,18 @@ func TestCreateResponse(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
assert.Equal(t, reqPubkey, res.Tags.GetFirst([]string{"p"}).Value())
|
||||
assert.Equal(t, reqEvent.ID, res.Tags.GetFirst([]string{"e"}).Value())
|
||||
assert.Equal(t, svc.cfg.NostrPublicKey, res.PubKey)
|
||||
assert.Equal(t, svc.cfg.GetNostrPublicKey(), res.PubKey)
|
||||
|
||||
decrypted, err := nip04.Decrypt(res.Content, ss)
|
||||
assert.NoError(t, err)
|
||||
unmarshalledResponse := Nip47Response{
|
||||
Result: &Nip47BalanceResponse{},
|
||||
unmarshalledResponse := nip47.Response{
|
||||
Result: &nip47.BalanceResponse{},
|
||||
}
|
||||
|
||||
err = json.Unmarshal([]byte(decrypted), &unmarshalledResponse)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, nip47Response.ResultType, unmarshalledResponse.ResultType)
|
||||
assert.Equal(t, nip47Response.Result, *unmarshalledResponse.Result.(*Nip47BalanceResponse))
|
||||
assert.Equal(t, nip47Response.Result, *unmarshalledResponse.Result.(*nip47.BalanceResponse))
|
||||
}
|
||||
|
||||
func TestHandleEncryption(t *testing.T) {}
|
||||
|
|
@ -420,7 +421,7 @@ func TestHandleMultiPayInvoiceEvent(t *testing.T) {
|
|||
app, ss, err := createApp(svc)
|
||||
assert.NoError(t, err)
|
||||
|
||||
request := &Nip47Request{}
|
||||
request := &nip47.Request{}
|
||||
err = json.Unmarshal([]byte(nip47MultiPayJson), request)
|
||||
assert.NoError(t, err)
|
||||
|
||||
|
|
@ -432,17 +433,17 @@ func TestHandleMultiPayInvoiceEvent(t *testing.T) {
|
|||
PubKey: app.NostrPubkey,
|
||||
Content: payload,
|
||||
}
|
||||
requestEvent := &RequestEvent{
|
||||
requestEvent := &db.RequestEvent{
|
||||
Content: reqEvent.Content,
|
||||
}
|
||||
|
||||
reqEvent.ID = "multi_pay_invoice_without_permission"
|
||||
requestEvent.NostrId = reqEvent.ID
|
||||
|
||||
responses := []*Nip47Response{}
|
||||
responses := []*nip47.Response{}
|
||||
dTags := []nostr.Tags{}
|
||||
|
||||
publishResponse := func(response *Nip47Response, tags nostr.Tags) {
|
||||
publishResponse := func(response *nip47.Response, tags nostr.Tags) {
|
||||
responses = append(responses, response)
|
||||
dTags = append(dTags, tags)
|
||||
}
|
||||
|
|
@ -460,7 +461,7 @@ func TestHandleMultiPayInvoiceEvent(t *testing.T) {
|
|||
maxAmount := 1000
|
||||
budgetRenewal := "never"
|
||||
expiresAt := time.Now().Add(24 * time.Hour)
|
||||
appPermission := &AppPermission{
|
||||
appPermission := &db.AppPermission{
|
||||
AppId: app.ID,
|
||||
App: *app,
|
||||
RequestMethod: nip47.PAY_INVOICE_METHOD,
|
||||
|
|
@ -473,13 +474,13 @@ func TestHandleMultiPayInvoiceEvent(t *testing.T) {
|
|||
|
||||
reqEvent.ID = "multi_pay_invoice_with_permission"
|
||||
requestEvent.NostrId = reqEvent.ID
|
||||
responses = []*Nip47Response{}
|
||||
responses = []*nip47.Response{}
|
||||
dTags = []nostr.Tags{}
|
||||
svc.HandleMultiPayInvoiceEvent(ctx, request, requestEvent, app, publishResponse)
|
||||
|
||||
assert.Equal(t, 2, len(responses))
|
||||
for i := 0; i < len(responses); i++ {
|
||||
assert.Equal(t, responses[i].Result.(Nip47PayResponse).Preimage, "123preimage")
|
||||
assert.Equal(t, responses[i].Result.(nip47.PayResponse).Preimage, "123preimage")
|
||||
assert.Equal(t, mockPaymentHash, dTags[i].GetFirst([]string{"d"}).Value())
|
||||
}
|
||||
|
||||
|
|
@ -493,7 +494,7 @@ func TestHandleMultiPayInvoiceEvent(t *testing.T) {
|
|||
|
||||
reqEvent.ID = "multi_pay_invoice_with_one_malformed_invoice"
|
||||
requestEvent.NostrId = reqEvent.ID
|
||||
responses = []*Nip47Response{}
|
||||
responses = []*nip47.Response{}
|
||||
dTags = []nostr.Tags{}
|
||||
svc.HandleMultiPayInvoiceEvent(ctx, request, requestEvent, app, publishResponse)
|
||||
|
||||
|
|
@ -502,13 +503,13 @@ func TestHandleMultiPayInvoiceEvent(t *testing.T) {
|
|||
assert.Equal(t, responses[0].Error.Code, nip47.ERROR_INTERNAL)
|
||||
|
||||
assert.Equal(t, mockPaymentHash, dTags[1].GetFirst([]string{"d"}).Value())
|
||||
assert.Equal(t, responses[1].Result.(Nip47PayResponse).Preimage, "123preimage")
|
||||
assert.Equal(t, responses[1].Result.(nip47.PayResponse).Preimage, "123preimage")
|
||||
|
||||
// we've spent 369 till here in three payments
|
||||
|
||||
// budget overflow
|
||||
newMaxAmount := 500
|
||||
err = svc.db.Model(&AppPermission{}).Where("app_id = ?", app.ID).Update("max_amount", newMaxAmount).Error
|
||||
err = svc.db.Model(&db.AppPermission{}).Where("app_id = ?", app.ID).Update("max_amount", newMaxAmount).Error
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = json.Unmarshal([]byte(nip47MultiPayOneOverflowingBudgetJson), request)
|
||||
|
|
@ -520,7 +521,7 @@ func TestHandleMultiPayInvoiceEvent(t *testing.T) {
|
|||
|
||||
reqEvent.ID = "multi_pay_invoice_with_budget_overflow"
|
||||
requestEvent.NostrId = reqEvent.ID
|
||||
responses = []*Nip47Response{}
|
||||
responses = []*nip47.Response{}
|
||||
dTags = []nostr.Tags{}
|
||||
svc.HandleMultiPayInvoiceEvent(ctx, request, requestEvent, app, publishResponse)
|
||||
|
||||
|
|
@ -529,7 +530,7 @@ func TestHandleMultiPayInvoiceEvent(t *testing.T) {
|
|||
// publishResponse as it's called earlier
|
||||
assert.Equal(t, responses[0].Error.Code, nip47.ERROR_QUOTA_EXCEEDED)
|
||||
assert.Equal(t, mockPaymentHash500, dTags[0].GetFirst([]string{"d"}).Value())
|
||||
assert.Equal(t, responses[1].Result.(Nip47PayResponse).Preimage, "123preimage")
|
||||
assert.Equal(t, responses[1].Result.(nip47.PayResponse).Preimage, "123preimage")
|
||||
assert.Equal(t, mockPaymentHash, dTags[1].GetFirst([]string{"d"}).Value())
|
||||
}
|
||||
|
||||
|
|
@ -544,7 +545,7 @@ func TestHandleMultiPayKeysendEvent(t *testing.T) {
|
|||
app, ss, err := createApp(svc)
|
||||
assert.NoError(t, err)
|
||||
|
||||
request := &Nip47Request{}
|
||||
request := &nip47.Request{}
|
||||
err = json.Unmarshal([]byte(nip47MultiPayKeysendJson), request)
|
||||
assert.NoError(t, err)
|
||||
|
||||
|
|
@ -556,17 +557,17 @@ func TestHandleMultiPayKeysendEvent(t *testing.T) {
|
|||
PubKey: app.NostrPubkey,
|
||||
Content: payload,
|
||||
}
|
||||
requestEvent := &RequestEvent{
|
||||
requestEvent := &db.RequestEvent{
|
||||
Content: reqEvent.Content,
|
||||
}
|
||||
|
||||
reqEvent.ID = "multi_pay_keysend_without_permission"
|
||||
requestEvent.NostrId = reqEvent.ID
|
||||
|
||||
responses := []*Nip47Response{}
|
||||
responses := []*nip47.Response{}
|
||||
dTags := []nostr.Tags{}
|
||||
|
||||
publishResponse := func(response *Nip47Response, tags nostr.Tags) {
|
||||
publishResponse := func(response *nip47.Response, tags nostr.Tags) {
|
||||
responses = append(responses, response)
|
||||
dTags = append(dTags, tags)
|
||||
}
|
||||
|
|
@ -585,7 +586,7 @@ func TestHandleMultiPayKeysendEvent(t *testing.T) {
|
|||
// because we need the same permission for keysend although
|
||||
// it works even with nip47.PAY_KEYSEND_METHOD, see
|
||||
// https://github.com/getAlby/nostr-wallet-connect/issues/189
|
||||
appPermission := &AppPermission{
|
||||
appPermission := &db.AppPermission{
|
||||
AppId: app.ID,
|
||||
App: *app,
|
||||
RequestMethod: nip47.PAY_INVOICE_METHOD,
|
||||
|
|
@ -598,13 +599,13 @@ func TestHandleMultiPayKeysendEvent(t *testing.T) {
|
|||
|
||||
reqEvent.ID = "multi_pay_keysend_with_permission"
|
||||
requestEvent.NostrId = reqEvent.ID
|
||||
responses = []*Nip47Response{}
|
||||
responses = []*nip47.Response{}
|
||||
dTags = []nostr.Tags{}
|
||||
svc.HandleMultiPayKeysendEvent(ctx, request, requestEvent, app, publishResponse)
|
||||
|
||||
assert.Equal(t, 2, len(responses))
|
||||
for i := 0; i < len(responses); i++ {
|
||||
assert.Equal(t, responses[i].Result.(Nip47PayResponse).Preimage, "12345preimage")
|
||||
assert.Equal(t, responses[i].Result.(nip47.PayResponse).Preimage, "12345preimage")
|
||||
assert.Equal(t, "123pubkey", dTags[i].GetFirst([]string{"d"}).Value())
|
||||
}
|
||||
|
||||
|
|
@ -612,7 +613,7 @@ func TestHandleMultiPayKeysendEvent(t *testing.T) {
|
|||
|
||||
// budget overflow
|
||||
newMaxAmount := 500
|
||||
err = svc.db.Model(&AppPermission{}).Where("app_id = ?", app.ID).Update("max_amount", newMaxAmount).Error
|
||||
err = svc.db.Model(&db.AppPermission{}).Where("app_id = ?", app.ID).Update("max_amount", newMaxAmount).Error
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = json.Unmarshal([]byte(nip47MultiPayKeysendOneOverflowingBudgetJson), request)
|
||||
|
|
@ -624,13 +625,13 @@ func TestHandleMultiPayKeysendEvent(t *testing.T) {
|
|||
|
||||
reqEvent.ID = "multi_pay_keysend_with_budget_overflow"
|
||||
requestEvent.NostrId = reqEvent.ID
|
||||
responses = []*Nip47Response{}
|
||||
responses = []*nip47.Response{}
|
||||
dTags = []nostr.Tags{}
|
||||
svc.HandleMultiPayKeysendEvent(ctx, request, requestEvent, app, publishResponse)
|
||||
|
||||
assert.Equal(t, responses[0].Error.Code, nip47.ERROR_QUOTA_EXCEEDED)
|
||||
assert.Equal(t, "500pubkey", dTags[0].GetFirst([]string{"d"}).Value())
|
||||
assert.Equal(t, responses[1].Result.(Nip47PayResponse).Preimage, "12345preimage")
|
||||
assert.Equal(t, responses[1].Result.(nip47.PayResponse).Preimage, "12345preimage")
|
||||
assert.Equal(t, "customId", dTags[1].GetFirst([]string{"d"}).Value())
|
||||
}
|
||||
|
||||
|
|
@ -644,7 +645,7 @@ func TestHandleGetBalanceEvent(t *testing.T) {
|
|||
app, ss, err := createApp(svc)
|
||||
assert.NoError(t, err)
|
||||
|
||||
request := &Nip47Request{}
|
||||
request := &nip47.Request{}
|
||||
err = json.Unmarshal([]byte(nip47GetBalanceJson), request)
|
||||
assert.NoError(t, err)
|
||||
|
||||
|
|
@ -656,16 +657,16 @@ func TestHandleGetBalanceEvent(t *testing.T) {
|
|||
PubKey: app.NostrPubkey,
|
||||
Content: payload,
|
||||
}
|
||||
requestEvent := &RequestEvent{
|
||||
requestEvent := &db.RequestEvent{
|
||||
Content: reqEvent.Content,
|
||||
}
|
||||
|
||||
reqEvent.ID = "test_get_balance_without_permission"
|
||||
requestEvent.NostrId = reqEvent.ID
|
||||
|
||||
responses := []*Nip47Response{}
|
||||
responses := []*nip47.Response{}
|
||||
|
||||
publishResponse := func(response *Nip47Response, tags nostr.Tags) {
|
||||
publishResponse := func(response *nip47.Response, tags nostr.Tags) {
|
||||
responses = append(responses, response)
|
||||
}
|
||||
|
||||
|
|
@ -678,7 +679,7 @@ func TestHandleGetBalanceEvent(t *testing.T) {
|
|||
|
||||
// with permission
|
||||
expiresAt := time.Now().Add(24 * time.Hour)
|
||||
appPermission := &AppPermission{
|
||||
appPermission := &db.AppPermission{
|
||||
AppId: app.ID,
|
||||
App: *app,
|
||||
RequestMethod: nip47.GET_BALANCE_METHOD,
|
||||
|
|
@ -689,15 +690,15 @@ func TestHandleGetBalanceEvent(t *testing.T) {
|
|||
|
||||
reqEvent.ID = "test_get_balance_with_permission"
|
||||
requestEvent.NostrId = reqEvent.ID
|
||||
responses = []*Nip47Response{}
|
||||
responses = []*nip47.Response{}
|
||||
svc.HandleGetBalanceEvent(ctx, request, requestEvent, app, publishResponse)
|
||||
|
||||
assert.Equal(t, responses[0].Result.(*Nip47BalanceResponse).Balance, int64(21000))
|
||||
assert.Equal(t, responses[0].Result.(*nip47.BalanceResponse).Balance, int64(21000))
|
||||
|
||||
// create pay_invoice permission
|
||||
maxAmount := 1000
|
||||
budgetRenewal := "never"
|
||||
appPermission = &AppPermission{
|
||||
appPermission = &db.AppPermission{
|
||||
AppId: app.ID,
|
||||
App: *app,
|
||||
RequestMethod: nip47.PAY_INVOICE_METHOD,
|
||||
|
|
@ -709,12 +710,12 @@ func TestHandleGetBalanceEvent(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
|
||||
reqEvent.ID = "test_get_balance_with_budget"
|
||||
responses = []*Nip47Response{}
|
||||
responses = []*nip47.Response{}
|
||||
svc.HandleGetBalanceEvent(ctx, request, requestEvent, app, publishResponse)
|
||||
|
||||
assert.Equal(t, int64(21000), responses[0].Result.(*Nip47BalanceResponse).Balance)
|
||||
assert.Equal(t, 1000000, responses[0].Result.(*Nip47BalanceResponse).MaxAmount)
|
||||
assert.Equal(t, "never", responses[0].Result.(*Nip47BalanceResponse).BudgetRenewal)
|
||||
assert.Equal(t, int64(21000), responses[0].Result.(*nip47.BalanceResponse).Balance)
|
||||
assert.Equal(t, 1000000, responses[0].Result.(*nip47.BalanceResponse).MaxAmount)
|
||||
assert.Equal(t, "never", responses[0].Result.(*nip47.BalanceResponse).BudgetRenewal)
|
||||
}
|
||||
|
||||
func TestHandlePayInvoiceEvent(t *testing.T) {
|
||||
|
|
@ -727,7 +728,7 @@ func TestHandlePayInvoiceEvent(t *testing.T) {
|
|||
app, ss, err := createApp(svc)
|
||||
assert.NoError(t, err)
|
||||
|
||||
request := &Nip47Request{}
|
||||
request := &nip47.Request{}
|
||||
err = json.Unmarshal([]byte(nip47PayJson), request)
|
||||
assert.NoError(t, err)
|
||||
|
||||
|
|
@ -739,16 +740,16 @@ func TestHandlePayInvoiceEvent(t *testing.T) {
|
|||
PubKey: app.NostrPubkey,
|
||||
Content: payload,
|
||||
}
|
||||
requestEvent := &RequestEvent{
|
||||
requestEvent := &db.RequestEvent{
|
||||
Content: reqEvent.Content,
|
||||
}
|
||||
|
||||
reqEvent.ID = "pay_invoice_without_permission"
|
||||
requestEvent.NostrId = reqEvent.ID
|
||||
|
||||
responses := []*Nip47Response{}
|
||||
responses := []*nip47.Response{}
|
||||
|
||||
publishResponse := func(response *Nip47Response, tags nostr.Tags) {
|
||||
publishResponse := func(response *nip47.Response, tags nostr.Tags) {
|
||||
responses = append(responses, response)
|
||||
}
|
||||
|
||||
|
|
@ -760,7 +761,7 @@ func TestHandlePayInvoiceEvent(t *testing.T) {
|
|||
maxAmount := 1000
|
||||
budgetRenewal := "never"
|
||||
expiresAt := time.Now().Add(24 * time.Hour)
|
||||
appPermission := &AppPermission{
|
||||
appPermission := &db.AppPermission{
|
||||
AppId: app.ID,
|
||||
App: *app,
|
||||
RequestMethod: nip47.PAY_INVOICE_METHOD,
|
||||
|
|
@ -773,10 +774,10 @@ func TestHandlePayInvoiceEvent(t *testing.T) {
|
|||
|
||||
reqEvent.ID = "pay_invoice_with_permission"
|
||||
requestEvent.NostrId = reqEvent.ID
|
||||
responses = []*Nip47Response{}
|
||||
responses = []*nip47.Response{}
|
||||
svc.HandlePayInvoiceEvent(ctx, request, requestEvent, app, publishResponse)
|
||||
|
||||
assert.Equal(t, responses[0].Result.(Nip47PayResponse).Preimage, "123preimage")
|
||||
assert.Equal(t, responses[0].Result.(nip47.PayResponse).Preimage, "123preimage")
|
||||
|
||||
// malformed invoice
|
||||
err = json.Unmarshal([]byte(nip47PayJsonNoInvoice), request)
|
||||
|
|
@ -788,7 +789,7 @@ func TestHandlePayInvoiceEvent(t *testing.T) {
|
|||
|
||||
reqEvent.ID = "pay_invoice_with_malformed_invoice"
|
||||
requestEvent.NostrId = reqEvent.ID
|
||||
responses = []*Nip47Response{}
|
||||
responses = []*nip47.Response{}
|
||||
svc.HandlePayInvoiceEvent(ctx, request, requestEvent, app, publishResponse)
|
||||
|
||||
assert.Equal(t, nip47.ERROR_INTERNAL, responses[0].Error.Code)
|
||||
|
|
@ -803,14 +804,14 @@ func TestHandlePayInvoiceEvent(t *testing.T) {
|
|||
|
||||
reqEvent.ID = "pay_invoice_with_wrong_request_method"
|
||||
requestEvent.NostrId = reqEvent.ID
|
||||
responses = []*Nip47Response{}
|
||||
responses = []*nip47.Response{}
|
||||
svc.HandlePayInvoiceEvent(ctx, request, requestEvent, app, publishResponse)
|
||||
|
||||
assert.Equal(t, nip47.ERROR_RESTRICTED, responses[0].Error.Code)
|
||||
|
||||
// budget overflow
|
||||
newMaxAmount := 100
|
||||
err = svc.db.Model(&AppPermission{}).Where("app_id = ?", app.ID).Update("max_amount", newMaxAmount).Error
|
||||
err = svc.db.Model(&db.AppPermission{}).Where("app_id = ?", app.ID).Update("max_amount", newMaxAmount).Error
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = json.Unmarshal([]byte(nip47PayJson), request)
|
||||
|
|
@ -822,33 +823,33 @@ func TestHandlePayInvoiceEvent(t *testing.T) {
|
|||
|
||||
reqEvent.ID = "pay_invoice_with_budget_overflow"
|
||||
requestEvent.NostrId = reqEvent.ID
|
||||
responses = []*Nip47Response{}
|
||||
responses = []*nip47.Response{}
|
||||
svc.HandlePayInvoiceEvent(ctx, request, requestEvent, app, publishResponse)
|
||||
|
||||
assert.Equal(t, nip47.ERROR_QUOTA_EXCEEDED, responses[0].Error.Code)
|
||||
|
||||
// budget expiry
|
||||
newExpiry := time.Now().Add(-24 * time.Hour)
|
||||
err = svc.db.Model(&AppPermission{}).Where("app_id = ?", app.ID).Update("max_amount", maxAmount).Update("expires_at", newExpiry).Error
|
||||
err = svc.db.Model(&db.AppPermission{}).Where("app_id = ?", app.ID).Update("max_amount", maxAmount).Update("expires_at", newExpiry).Error
|
||||
assert.NoError(t, err)
|
||||
|
||||
reqEvent.ID = "pay_invoice_with_budget_expiry"
|
||||
requestEvent.NostrId = reqEvent.ID
|
||||
responses = []*Nip47Response{}
|
||||
responses = []*nip47.Response{}
|
||||
svc.HandlePayInvoiceEvent(ctx, request, requestEvent, app, publishResponse)
|
||||
|
||||
assert.Equal(t, nip47.ERROR_EXPIRED, responses[0].Error.Code)
|
||||
|
||||
// check again
|
||||
err = svc.db.Model(&AppPermission{}).Where("app_id = ?", app.ID).Update("expires_at", nil).Error
|
||||
err = svc.db.Model(&db.AppPermission{}).Where("app_id = ?", app.ID).Update("expires_at", nil).Error
|
||||
assert.NoError(t, err)
|
||||
|
||||
reqEvent.ID = "pay_invoice_after_change"
|
||||
requestEvent.NostrId = reqEvent.ID
|
||||
responses = []*Nip47Response{}
|
||||
responses = []*nip47.Response{}
|
||||
svc.HandlePayInvoiceEvent(ctx, request, requestEvent, app, publishResponse)
|
||||
|
||||
assert.Equal(t, responses[0].Result.(Nip47PayResponse).Preimage, "123preimage")
|
||||
assert.Equal(t, responses[0].Result.(nip47.PayResponse).Preimage, "123preimage")
|
||||
}
|
||||
|
||||
func TestHandlePayKeysendEvent(t *testing.T) {
|
||||
|
|
@ -861,7 +862,7 @@ func TestHandlePayKeysendEvent(t *testing.T) {
|
|||
app, ss, err := createApp(svc)
|
||||
assert.NoError(t, err)
|
||||
|
||||
request := &Nip47Request{}
|
||||
request := &nip47.Request{}
|
||||
err = json.Unmarshal([]byte(nip47KeysendJson), request)
|
||||
assert.NoError(t, err)
|
||||
|
||||
|
|
@ -873,16 +874,16 @@ func TestHandlePayKeysendEvent(t *testing.T) {
|
|||
PubKey: app.NostrPubkey,
|
||||
Content: payload,
|
||||
}
|
||||
requestEvent := &RequestEvent{
|
||||
requestEvent := &db.RequestEvent{
|
||||
Content: reqEvent.Content,
|
||||
}
|
||||
|
||||
reqEvent.ID = "pay_keysend_without_permission"
|
||||
requestEvent.NostrId = reqEvent.ID
|
||||
|
||||
responses := []*Nip47Response{}
|
||||
responses := []*nip47.Response{}
|
||||
|
||||
publishResponse := func(response *Nip47Response, tags nostr.Tags) {
|
||||
publishResponse := func(response *nip47.Response, tags nostr.Tags) {
|
||||
responses = append(responses, response)
|
||||
}
|
||||
|
||||
|
|
@ -897,7 +898,7 @@ func TestHandlePayKeysendEvent(t *testing.T) {
|
|||
// because we need the same permission for keysend although
|
||||
// it works even with nip47.PAY_KEYSEND_METHOD, see
|
||||
// https://github.com/getAlby/nostr-wallet-connect/issues/189
|
||||
appPermission := &AppPermission{
|
||||
appPermission := &db.AppPermission{
|
||||
AppId: app.ID,
|
||||
App: *app,
|
||||
RequestMethod: nip47.PAY_INVOICE_METHOD,
|
||||
|
|
@ -910,14 +911,14 @@ func TestHandlePayKeysendEvent(t *testing.T) {
|
|||
|
||||
reqEvent.ID = "pay_keysend_with_permission"
|
||||
requestEvent.NostrId = reqEvent.ID
|
||||
responses = []*Nip47Response{}
|
||||
responses = []*nip47.Response{}
|
||||
svc.HandlePayKeysendEvent(ctx, request, requestEvent, app, publishResponse)
|
||||
|
||||
assert.Equal(t, responses[0].Result.(Nip47PayResponse).Preimage, "12345preimage")
|
||||
assert.Equal(t, responses[0].Result.(nip47.PayResponse).Preimage, "12345preimage")
|
||||
|
||||
// budget overflow
|
||||
newMaxAmount := 100
|
||||
err = svc.db.Model(&AppPermission{}).Where("app_id = ?", app.ID).Update("max_amount", newMaxAmount).Error
|
||||
err = svc.db.Model(&db.AppPermission{}).Where("app_id = ?", app.ID).Update("max_amount", newMaxAmount).Error
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = json.Unmarshal([]byte(nip47KeysendJson), request)
|
||||
|
|
@ -929,7 +930,7 @@ func TestHandlePayKeysendEvent(t *testing.T) {
|
|||
|
||||
reqEvent.ID = "pay_keysend_with_budget_overflow"
|
||||
requestEvent.NostrId = reqEvent.ID
|
||||
responses = []*Nip47Response{}
|
||||
responses = []*nip47.Response{}
|
||||
svc.HandlePayKeysendEvent(ctx, request, requestEvent, app, publishResponse)
|
||||
|
||||
assert.Equal(t, nip47.ERROR_QUOTA_EXCEEDED, responses[0].Error.Code)
|
||||
|
|
@ -945,7 +946,7 @@ func TestHandleLookupInvoiceEvent(t *testing.T) {
|
|||
app, ss, err := createApp(svc)
|
||||
assert.NoError(t, err)
|
||||
|
||||
request := &Nip47Request{}
|
||||
request := &nip47.Request{}
|
||||
err = json.Unmarshal([]byte(nip47LookupInvoiceJson), request)
|
||||
assert.NoError(t, err)
|
||||
|
||||
|
|
@ -957,16 +958,16 @@ func TestHandleLookupInvoiceEvent(t *testing.T) {
|
|||
PubKey: app.NostrPubkey,
|
||||
Content: payload,
|
||||
}
|
||||
requestEvent := &RequestEvent{
|
||||
requestEvent := &db.RequestEvent{
|
||||
Content: reqEvent.Content,
|
||||
}
|
||||
|
||||
reqEvent.ID = "test_lookup_invoice_without_permission"
|
||||
requestEvent.NostrId = reqEvent.ID
|
||||
|
||||
responses := []*Nip47Response{}
|
||||
responses := []*nip47.Response{}
|
||||
|
||||
publishResponse := func(response *Nip47Response, tags nostr.Tags) {
|
||||
publishResponse := func(response *nip47.Response, tags nostr.Tags) {
|
||||
responses = append(responses, response)
|
||||
}
|
||||
|
||||
|
|
@ -976,7 +977,7 @@ func TestHandleLookupInvoiceEvent(t *testing.T) {
|
|||
|
||||
// with permission
|
||||
expiresAt := time.Now().Add(24 * time.Hour)
|
||||
appPermission := &AppPermission{
|
||||
appPermission := &db.AppPermission{
|
||||
AppId: app.ID,
|
||||
App: *app,
|
||||
RequestMethod: nip47.LOOKUP_INVOICE_METHOD,
|
||||
|
|
@ -987,10 +988,10 @@ func TestHandleLookupInvoiceEvent(t *testing.T) {
|
|||
|
||||
reqEvent.ID = "test_lookup_invoice_with_permission"
|
||||
requestEvent.NostrId = reqEvent.ID
|
||||
responses = []*Nip47Response{}
|
||||
responses = []*nip47.Response{}
|
||||
svc.HandleLookupInvoiceEvent(ctx, request, requestEvent, app, publishResponse)
|
||||
|
||||
transaction := responses[0].Result.(*Nip47LookupInvoiceResponse)
|
||||
transaction := responses[0].Result.(*nip47.LookupInvoiceResponse)
|
||||
assert.Equal(t, mockTransaction.Type, transaction.Type)
|
||||
assert.Equal(t, mockTransaction.Invoice, transaction.Invoice)
|
||||
assert.Equal(t, mockTransaction.Description, transaction.Description)
|
||||
|
|
@ -1012,7 +1013,7 @@ func TestHandleMakeInvoiceEvent(t *testing.T) {
|
|||
app, ss, err := createApp(svc)
|
||||
assert.NoError(t, err)
|
||||
|
||||
request := &Nip47Request{}
|
||||
request := &nip47.Request{}
|
||||
err = json.Unmarshal([]byte(nip47MakeInvoiceJson), request)
|
||||
assert.NoError(t, err)
|
||||
|
||||
|
|
@ -1024,16 +1025,16 @@ func TestHandleMakeInvoiceEvent(t *testing.T) {
|
|||
PubKey: app.NostrPubkey,
|
||||
Content: payload,
|
||||
}
|
||||
requestEvent := &RequestEvent{
|
||||
requestEvent := &db.RequestEvent{
|
||||
Content: reqEvent.Content,
|
||||
}
|
||||
|
||||
reqEvent.ID = "test_make_invoice_without_permission"
|
||||
requestEvent.NostrId = reqEvent.ID
|
||||
|
||||
responses := []*Nip47Response{}
|
||||
responses := []*nip47.Response{}
|
||||
|
||||
publishResponse := func(response *Nip47Response, tags nostr.Tags) {
|
||||
publishResponse := func(response *nip47.Response, tags nostr.Tags) {
|
||||
responses = append(responses, response)
|
||||
}
|
||||
|
||||
|
|
@ -1043,7 +1044,7 @@ func TestHandleMakeInvoiceEvent(t *testing.T) {
|
|||
|
||||
// with permission
|
||||
expiresAt := time.Now().Add(24 * time.Hour)
|
||||
appPermission := &AppPermission{
|
||||
appPermission := &db.AppPermission{
|
||||
AppId: app.ID,
|
||||
App: *app,
|
||||
RequestMethod: nip47.MAKE_INVOICE_METHOD,
|
||||
|
|
@ -1054,10 +1055,10 @@ func TestHandleMakeInvoiceEvent(t *testing.T) {
|
|||
|
||||
reqEvent.ID = "test_make_invoice_with_permission"
|
||||
requestEvent.NostrId = reqEvent.ID
|
||||
responses = []*Nip47Response{}
|
||||
responses = []*nip47.Response{}
|
||||
svc.HandleMakeInvoiceEvent(ctx, request, requestEvent, app, publishResponse)
|
||||
|
||||
assert.Equal(t, mockTransaction.Preimage, responses[0].Result.(*Nip47MakeInvoiceResponse).Preimage)
|
||||
assert.Equal(t, mockTransaction.Preimage, responses[0].Result.(*nip47.MakeInvoiceResponse).Preimage)
|
||||
}
|
||||
|
||||
func TestHandleListTransactionsEvent(t *testing.T) {
|
||||
|
|
@ -1070,7 +1071,7 @@ func TestHandleListTransactionsEvent(t *testing.T) {
|
|||
app, ss, err := createApp(svc)
|
||||
assert.NoError(t, err)
|
||||
|
||||
request := &Nip47Request{}
|
||||
request := &nip47.Request{}
|
||||
err = json.Unmarshal([]byte(nip47ListTransactionsJson), request)
|
||||
assert.NoError(t, err)
|
||||
|
||||
|
|
@ -1082,16 +1083,16 @@ func TestHandleListTransactionsEvent(t *testing.T) {
|
|||
PubKey: app.NostrPubkey,
|
||||
Content: payload,
|
||||
}
|
||||
requestEvent := &RequestEvent{
|
||||
requestEvent := &db.RequestEvent{
|
||||
Content: reqEvent.Content,
|
||||
}
|
||||
|
||||
reqEvent.ID = "test_list_transactions_without_permission"
|
||||
requestEvent.NostrId = reqEvent.ID
|
||||
|
||||
responses := []*Nip47Response{}
|
||||
responses := []*nip47.Response{}
|
||||
|
||||
publishResponse := func(response *Nip47Response, tags nostr.Tags) {
|
||||
publishResponse := func(response *nip47.Response, tags nostr.Tags) {
|
||||
responses = append(responses, response)
|
||||
}
|
||||
|
||||
|
|
@ -1101,7 +1102,7 @@ func TestHandleListTransactionsEvent(t *testing.T) {
|
|||
|
||||
// with permission
|
||||
expiresAt := time.Now().Add(24 * time.Hour)
|
||||
appPermission := &AppPermission{
|
||||
appPermission := &db.AppPermission{
|
||||
AppId: app.ID,
|
||||
App: *app,
|
||||
RequestMethod: nip47.LIST_TRANSACTIONS_METHOD,
|
||||
|
|
@ -1112,11 +1113,11 @@ func TestHandleListTransactionsEvent(t *testing.T) {
|
|||
|
||||
reqEvent.ID = "test_list_transactions_with_permission"
|
||||
requestEvent.NostrId = reqEvent.ID
|
||||
responses = []*Nip47Response{}
|
||||
responses = []*nip47.Response{}
|
||||
svc.HandleListTransactionsEvent(ctx, request, requestEvent, app, publishResponse)
|
||||
|
||||
assert.Equal(t, 2, len(responses[0].Result.(*Nip47ListTransactionsResponse).Transactions))
|
||||
transaction := responses[0].Result.(*Nip47ListTransactionsResponse).Transactions[0]
|
||||
assert.Equal(t, 2, len(responses[0].Result.(*nip47.ListTransactionsResponse).Transactions))
|
||||
transaction := responses[0].Result.(*nip47.ListTransactionsResponse).Transactions[0]
|
||||
assert.Equal(t, mockTransactions[0].Type, transaction.Type)
|
||||
assert.Equal(t, mockTransactions[0].Invoice, transaction.Invoice)
|
||||
assert.Equal(t, mockTransactions[0].Description, transaction.Description)
|
||||
|
|
@ -1138,7 +1139,7 @@ func TestHandleGetInfoEvent(t *testing.T) {
|
|||
app, ss, err := createApp(svc)
|
||||
assert.NoError(t, err)
|
||||
|
||||
request := &Nip47Request{}
|
||||
request := &nip47.Request{}
|
||||
err = json.Unmarshal([]byte(nip47GetInfoJson), request)
|
||||
assert.NoError(t, err)
|
||||
|
||||
|
|
@ -1150,16 +1151,16 @@ func TestHandleGetInfoEvent(t *testing.T) {
|
|||
PubKey: app.NostrPubkey,
|
||||
Content: payload,
|
||||
}
|
||||
requestEvent := &RequestEvent{
|
||||
requestEvent := &db.RequestEvent{
|
||||
Content: reqEvent.Content,
|
||||
}
|
||||
|
||||
reqEvent.ID = "test_get_info_without_permission"
|
||||
requestEvent.NostrId = reqEvent.ID
|
||||
|
||||
responses := []*Nip47Response{}
|
||||
responses := []*nip47.Response{}
|
||||
|
||||
publishResponse := func(response *Nip47Response, tags nostr.Tags) {
|
||||
publishResponse := func(response *nip47.Response, tags nostr.Tags) {
|
||||
responses = append(responses, response)
|
||||
}
|
||||
|
||||
|
|
@ -1168,7 +1169,7 @@ func TestHandleGetInfoEvent(t *testing.T) {
|
|||
assert.Equal(t, nip47.ERROR_RESTRICTED, responses[0].Error.Code)
|
||||
|
||||
expiresAt := time.Now().Add(24 * time.Hour)
|
||||
appPermission := &AppPermission{
|
||||
appPermission := &db.AppPermission{
|
||||
AppId: app.ID,
|
||||
App: *app,
|
||||
RequestMethod: nip47.GET_INFO_METHOD,
|
||||
|
|
@ -1179,10 +1180,10 @@ func TestHandleGetInfoEvent(t *testing.T) {
|
|||
|
||||
reqEvent.ID = "test_get_info_with_permission"
|
||||
requestEvent.NostrId = reqEvent.ID
|
||||
responses = []*Nip47Response{}
|
||||
responses = []*nip47.Response{}
|
||||
svc.HandleGetInfoEvent(ctx, request, requestEvent, app, publishResponse)
|
||||
|
||||
nodeInfo := responses[0].Result.(*Nip47GetInfoResponse)
|
||||
nodeInfo := responses[0].Result.(*nip47.GetInfoResponse)
|
||||
assert.Equal(t, mockNodeInfo.Alias, nodeInfo.Alias)
|
||||
assert.Equal(t, mockNodeInfo.Color, nodeInfo.Color)
|
||||
assert.Equal(t, mockNodeInfo.Pubkey, nodeInfo.Pubkey)
|
||||
|
|
@ -1203,44 +1204,45 @@ func createTestService(ln *MockLn) (svc *Service, err error) {
|
|||
logger.SetOutput(os.Stdout)
|
||||
logger.SetLevel(logrus.InfoLevel)
|
||||
|
||||
err = migrations.Migrate(gormDb, &config.AppConfig{
|
||||
appConfig := &config.AppConfig{
|
||||
Workdir: ".test",
|
||||
}, logger)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sk := nostr.GeneratePrivateKey()
|
||||
pk, err := nostr.GetPublicKey(sk)
|
||||
|
||||
err = migrations.Migrate(gormDb, appConfig, logger)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cfg := config.NewConfig(
|
||||
gormDb,
|
||||
appConfig,
|
||||
logger,
|
||||
)
|
||||
|
||||
cfg.Start("")
|
||||
|
||||
return &Service{
|
||||
cfg: &Config{
|
||||
db: gormDb,
|
||||
NostrSecretKey: sk,
|
||||
NostrPublicKey: pk,
|
||||
},
|
||||
cfg: cfg,
|
||||
db: gormDb,
|
||||
lnClient: ln,
|
||||
Logger: logger,
|
||||
EventPublisher: events.NewEventPublisher(logger),
|
||||
logger: logger,
|
||||
eventPublisher: events.NewEventPublisher(logger),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func createApp(svc *Service) (app *App, ss []byte, err error) {
|
||||
func createApp(svc *Service) (app *db.App, ss []byte, err error) {
|
||||
senderPrivkey := nostr.GeneratePrivateKey()
|
||||
senderPubkey, err := nostr.GetPublicKey(senderPrivkey)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
ss, err = nip04.ComputeSharedSecret(svc.cfg.NostrPublicKey, senderPrivkey)
|
||||
ss, err = nip04.ComputeSharedSecret(svc.cfg.GetNostrPublicKey(), senderPrivkey)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
app = &App{Name: "test", NostrPubkey: senderPubkey}
|
||||
app = &db.App{Name: "test", NostrPubkey: senderPubkey}
|
||||
err = svc.db.Create(app).Error
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
|
|
@ -1256,8 +1258,8 @@ func NewMockLn() (*MockLn, error) {
|
|||
return &MockLn{}, nil
|
||||
}
|
||||
|
||||
func (mln *MockLn) SendPaymentSync(ctx context.Context, payReq string) (*lnclient.Nip47PayInvoiceResponse, error) {
|
||||
return &lnclient.Nip47PayInvoiceResponse{
|
||||
func (mln *MockLn) SendPaymentSync(ctx context.Context, payReq string) (*lnclient.PayInvoiceResponse, error) {
|
||||
return &lnclient.PayInvoiceResponse{
|
||||
Preimage: "123preimage",
|
||||
}, nil
|
||||
}
|
||||
|
|
@ -1274,15 +1276,15 @@ func (mln *MockLn) GetInfo(ctx context.Context) (info *lnclient.NodeInfo, err er
|
|||
return &mockNodeInfo, nil
|
||||
}
|
||||
|
||||
func (mln *MockLn) MakeInvoice(ctx context.Context, amount int64, description string, descriptionHash string, expiry int64) (transaction *Nip47Transaction, err error) {
|
||||
func (mln *MockLn) MakeInvoice(ctx context.Context, amount int64, description string, descriptionHash string, expiry int64) (transaction *nip47.Transaction, err error) {
|
||||
return mockTransaction, nil
|
||||
}
|
||||
|
||||
func (mln *MockLn) LookupInvoice(ctx context.Context, paymentHash string) (transaction *Nip47Transaction, err error) {
|
||||
func (mln *MockLn) LookupInvoice(ctx context.Context, paymentHash string) (transaction *nip47.Transaction, err error) {
|
||||
return mockTransaction, nil
|
||||
}
|
||||
|
||||
func (mln *MockLn) ListTransactions(ctx context.Context, from, until, limit, offset uint64, unpaid bool, invoiceType string) (invoices []Nip47Transaction, err error) {
|
||||
func (mln *MockLn) ListTransactions(ctx context.Context, from, until, limit, offset uint64, unpaid bool, invoiceType string) (invoices []nip47.Transaction, err error) {
|
||||
return mockTransactions, nil
|
||||
}
|
||||
func (mln *MockLn) Shutdown() error {
|
||||
|
|
@ -1343,3 +1345,4 @@ func (mln *MockLn) GetNodeStatus(ctx context.Context) (nodeStatus *lnclient.Node
|
|||
func (mln *MockLn) GetNetworkGraph(nodeIds []string) (lnclient.NetworkGraphResponse, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (mln *MockLn) UpdateLastWalletSyncRequest() {}
|
||||
|
|
|
|||
64
start.go
64
start.go
|
|
@ -7,31 +7,28 @@ import (
|
|||
|
||||
"github.com/nbd-wtf/go-nostr"
|
||||
"github.com/nbd-wtf/go-nostr/nip19"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/getAlby/nostr-wallet-connect/events"
|
||||
)
|
||||
|
||||
func (svc *Service) StartNostr(ctx context.Context, encryptionKey string) error {
|
||||
relayUrl, _ := svc.cfg.Get("Relay", encryptionKey)
|
||||
nostrSecretKey, _ := svc.cfg.Get("NostrSecretKey", encryptionKey)
|
||||
if nostrSecretKey == "" {
|
||||
nostrSecretKey = nostr.GeneratePrivateKey()
|
||||
svc.cfg.SetUpdate("NostrSecretKey", nostrSecretKey, encryptionKey)
|
||||
}
|
||||
nostrPublicKey, err := nostr.GetPublicKey(nostrSecretKey)
|
||||
if err != nil {
|
||||
svc.Logger.Errorf("Error converting nostr privkey to pubkey: %v", err)
|
||||
return err
|
||||
}
|
||||
svc.cfg.NostrSecretKey = nostrSecretKey
|
||||
svc.cfg.NostrPublicKey = nostrPublicKey
|
||||
relayUrl := svc.cfg.GetRelayUrl()
|
||||
|
||||
npub, err := nip19.EncodePublicKey(svc.cfg.NostrPublicKey)
|
||||
err := svc.cfg.Start(encryptionKey)
|
||||
if err != nil {
|
||||
svc.Logger.Fatalf("Error converting nostr privkey to pubkey: %v", err)
|
||||
svc.logger.WithError(err).Fatal("Failed to start config")
|
||||
}
|
||||
|
||||
svc.Logger.Infof("Starting nostr-wallet-connect. npub: %s hex: %s", npub, svc.cfg.NostrPublicKey)
|
||||
npub, err := nip19.EncodePublicKey(svc.cfg.GetNostrPublicKey())
|
||||
if err != nil {
|
||||
svc.logger.WithError(err).Fatal("Error converting nostr privkey to pubkey")
|
||||
}
|
||||
|
||||
svc.logger.WithFields(logrus.Fields{
|
||||
"npub": npub,
|
||||
"hex": svc.cfg.GetNostrPublicKey(),
|
||||
}).Info("Starting nostr-wallet-connect")
|
||||
svc.wg.Add(1)
|
||||
go func() {
|
||||
//Start infinite loop which will be only broken by canceling ctx (SIGINT)
|
||||
|
|
@ -43,11 +40,11 @@ func (svc *Service) StartNostr(ctx context.Context, encryptionKey string) error
|
|||
if i > 0 {
|
||||
sleepDuration := 10
|
||||
contextCancelled := false
|
||||
svc.Logger.Infof("[Iteration %d] Retrying in %d seconds...", i, sleepDuration)
|
||||
svc.logger.Infof("[Iteration %d] Retrying in %d seconds...", i, sleepDuration)
|
||||
|
||||
select {
|
||||
case <-ctx.Done(): //context cancelled
|
||||
svc.Logger.Info("service context cancelled while waiting for retry")
|
||||
svc.logger.Info("service context cancelled while waiting for retry")
|
||||
contextCancelled = true
|
||||
case <-time.After(time.Duration(sleepDuration) * time.Second): //timeout
|
||||
}
|
||||
|
|
@ -58,49 +55,49 @@ func (svc *Service) StartNostr(ctx context.Context, encryptionKey string) error
|
|||
if relay != nil && relay.IsConnected() {
|
||||
err := relay.Close()
|
||||
if err != nil {
|
||||
svc.Logger.WithError(err).Error("Could not close relay connection")
|
||||
svc.logger.WithError(err).Error("Could not close relay connection")
|
||||
}
|
||||
}
|
||||
|
||||
//connect to the relay
|
||||
svc.Logger.Infof("Connecting to the relay: %s", relayUrl)
|
||||
svc.logger.Infof("Connecting to the relay: %s", relayUrl)
|
||||
|
||||
relay, err := nostr.RelayConnect(ctx, relayUrl, nostr.WithNoticeHandler(svc.noticeHandler))
|
||||
if err != nil {
|
||||
svc.Logger.WithError(err).Error("Failed to connect to relay")
|
||||
svc.logger.WithError(err).Error("Failed to connect to relay")
|
||||
continue
|
||||
}
|
||||
|
||||
//publish event with NIP-47 info
|
||||
err = svc.PublishNip47Info(ctx, relay)
|
||||
if err != nil {
|
||||
svc.Logger.WithError(err).Error("Could not publish NIP47 info")
|
||||
svc.logger.WithError(err).Error("Could not publish NIP47 info")
|
||||
}
|
||||
|
||||
svc.Logger.Info("Subscribing to events")
|
||||
sub, err := relay.Subscribe(ctx, svc.createFilters(svc.cfg.NostrPublicKey))
|
||||
svc.logger.Info("Subscribing to events")
|
||||
sub, err := relay.Subscribe(ctx, svc.createFilters(svc.cfg.GetNostrPublicKey()))
|
||||
if err != nil {
|
||||
svc.Logger.WithError(err).Error("Failed to subscribe to events")
|
||||
svc.logger.WithError(err).Error("Failed to subscribe to events")
|
||||
continue
|
||||
}
|
||||
err = svc.StartSubscription(sub.Context, sub)
|
||||
if err != nil {
|
||||
//err being non-nil means that we have an error on the websocket error channel. In this case we just try to reconnect.
|
||||
svc.Logger.WithError(err).Error("Got an error from the relay while listening to subscription.")
|
||||
svc.logger.WithError(err).Error("Got an error from the relay while listening to subscription.")
|
||||
continue
|
||||
}
|
||||
//err being nil means that the context was canceled and we should exit the program.
|
||||
break
|
||||
}
|
||||
svc.Logger.Info("Disconnecting from relay...")
|
||||
svc.logger.Info("Disconnecting from relay...")
|
||||
if relay != nil && relay.IsConnected() {
|
||||
err := relay.Close()
|
||||
if err != nil {
|
||||
svc.Logger.WithError(err).Error("Could not close relay connection")
|
||||
svc.logger.WithError(err).Error("Could not close relay connection")
|
||||
}
|
||||
}
|
||||
svc.Shutdown()
|
||||
svc.Logger.Info("Relay subroutine ended")
|
||||
svc.logger.Info("Relay subroutine ended")
|
||||
svc.wg.Done()
|
||||
}()
|
||||
return nil
|
||||
|
|
@ -108,7 +105,7 @@ func (svc *Service) StartNostr(ctx context.Context, encryptionKey string) error
|
|||
|
||||
func (svc *Service) StartApp(encryptionKey string) error {
|
||||
if !svc.cfg.CheckUnlockPassword(encryptionKey) {
|
||||
svc.Logger.Errorf("Invalid password")
|
||||
svc.logger.Errorf("Invalid password")
|
||||
return errors.New("invalid password")
|
||||
}
|
||||
|
||||
|
|
@ -116,8 +113,8 @@ func (svc *Service) StartApp(encryptionKey string) error {
|
|||
|
||||
err := svc.launchLNBackend(ctx, encryptionKey)
|
||||
if err != nil {
|
||||
svc.Logger.Errorf("Failed to launch LN backend: %v", err)
|
||||
svc.EventPublisher.Publish(&events.Event{
|
||||
svc.logger.Errorf("Failed to launch LN backend: %v", err)
|
||||
svc.eventPublisher.Publish(&events.Event{
|
||||
Event: "nwc_node_start_failed",
|
||||
})
|
||||
cancelFn()
|
||||
|
|
@ -129,6 +126,7 @@ func (svc *Service) StartApp(encryptionKey string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// TODO: remove and call StopLNClient() instead
|
||||
func (svc *Service) StopApp() {
|
||||
if svc.appCancelFn != nil {
|
||||
svc.appCancelFn()
|
||||
|
|
@ -138,7 +136,7 @@ func (svc *Service) StopApp() {
|
|||
|
||||
func (svc *Service) Shutdown() {
|
||||
svc.StopLNClient()
|
||||
svc.EventPublisher.Publish(&events.Event{
|
||||
svc.eventPublisher.Publish(&events.Event{
|
||||
Event: "nwc_stopped",
|
||||
})
|
||||
// wait for any remaining events
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
|
@ -5,6 +5,7 @@ import (
|
|||
"embed"
|
||||
"log"
|
||||
|
||||
"github.com/getAlby/nostr-wallet-connect/api"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/wailsapp/wails/v2"
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
|
|
@ -17,13 +18,13 @@ var assets embed.FS
|
|||
type WailsApp struct {
|
||||
ctx context.Context
|
||||
svc *Service
|
||||
api *API
|
||||
api api.API
|
||||
}
|
||||
|
||||
func NewApp(svc *Service) *WailsApp {
|
||||
return &WailsApp{
|
||||
svc: svc,
|
||||
api: NewAPI(svc),
|
||||
api: api.NewAPI(svc, svc.logger, svc.db),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -34,7 +35,7 @@ func (app *WailsApp) startup(ctx context.Context) {
|
|||
}
|
||||
|
||||
func LaunchWailsApp(app *WailsApp) {
|
||||
logger := NewWailsLogger(app.svc.Logger)
|
||||
logger := NewWailsLogger(app.svc.logger)
|
||||
|
||||
err := wails.Run(&options.App{
|
||||
Title: "Nostr Wallet Connect",
|
||||
|
|
|
|||
|
|
@ -9,7 +9,10 @@ import (
|
|||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/getAlby/nostr-wallet-connect/models/api"
|
||||
"github.com/getAlby/nostr-wallet-connect/alby"
|
||||
"github.com/getAlby/nostr-wallet-connect/api"
|
||||
"github.com/getAlby/nostr-wallet-connect/db"
|
||||
"github.com/getAlby/nostr-wallet-connect/lsp"
|
||||
"github.com/wailsapp/wails/v2/pkg/runtime"
|
||||
)
|
||||
|
||||
|
|
@ -33,9 +36,9 @@ func (app *WailsApp) WailsRequestRouter(route string, method string, body string
|
|||
case len(authCodeMatch) > 1:
|
||||
code := authCodeMatch[1]
|
||||
|
||||
err := app.svc.AlbyOAuthSvc.CallbackHandler(ctx, code)
|
||||
err := app.svc.albyOAuthSvc.CallbackHandler(ctx, code)
|
||||
if err != nil {
|
||||
app.svc.Logger.WithFields(logrus.Fields{
|
||||
app.svc.logger.WithFields(logrus.Fields{
|
||||
"route": route,
|
||||
"method": method,
|
||||
"body": body,
|
||||
|
|
@ -55,7 +58,7 @@ func (app *WailsApp) WailsRequestRouter(route string, method string, body string
|
|||
case len(appMatch) > 1:
|
||||
pubkey := appMatch[1]
|
||||
|
||||
userApp := App{}
|
||||
userApp := db.App{}
|
||||
findResult := app.svc.db.Where("nostr_pubkey = ?", pubkey).First(&userApp)
|
||||
|
||||
if findResult.RowsAffected == 0 {
|
||||
|
|
@ -70,7 +73,7 @@ func (app *WailsApp) WailsRequestRouter(route string, method string, body string
|
|||
updateAppRequest := &api.UpdateAppRequest{}
|
||||
err := json.Unmarshal([]byte(body), updateAppRequest)
|
||||
if err != nil {
|
||||
app.svc.Logger.WithFields(logrus.Fields{
|
||||
app.svc.logger.WithFields(logrus.Fields{
|
||||
"route": route,
|
||||
"method": method,
|
||||
"body": body,
|
||||
|
|
@ -146,9 +149,9 @@ func (app *WailsApp) WailsRequestRouter(route string, method string, body string
|
|||
|
||||
switch route {
|
||||
case "/api/alby/me":
|
||||
me, err := app.svc.AlbyOAuthSvc.GetMe(ctx)
|
||||
me, err := app.svc.albyOAuthSvc.GetMe(ctx)
|
||||
if err != nil {
|
||||
app.svc.Logger.WithFields(logrus.Fields{
|
||||
app.svc.logger.WithFields(logrus.Fields{
|
||||
"route": route,
|
||||
"method": method,
|
||||
"body": body,
|
||||
|
|
@ -157,30 +160,30 @@ func (app *WailsApp) WailsRequestRouter(route string, method string, body string
|
|||
}
|
||||
return WailsRequestRouterResponse{Body: me, Error: ""}
|
||||
case "/api/alby/balance":
|
||||
balance, err := app.svc.AlbyOAuthSvc.GetBalance(ctx)
|
||||
balance, err := app.svc.albyOAuthSvc.GetBalance(ctx)
|
||||
if err != nil {
|
||||
app.svc.Logger.WithFields(logrus.Fields{
|
||||
app.svc.logger.WithFields(logrus.Fields{
|
||||
"route": route,
|
||||
"method": method,
|
||||
"body": body,
|
||||
}).WithError(err).Error("Failed to decode request to wails router")
|
||||
return WailsRequestRouterResponse{Body: nil, Error: err.Error()}
|
||||
}
|
||||
return WailsRequestRouterResponse{Body: &api.AlbyBalanceResponse{
|
||||
return WailsRequestRouterResponse{Body: &alby.AlbyBalanceResponse{
|
||||
Sats: balance.Balance,
|
||||
}, Error: ""}
|
||||
case "/api/alby/pay":
|
||||
payRequest := &api.AlbyPayRequest{}
|
||||
payRequest := &alby.AlbyPayRequest{}
|
||||
err := json.Unmarshal([]byte(body), payRequest)
|
||||
if err != nil {
|
||||
app.svc.Logger.WithFields(logrus.Fields{
|
||||
app.svc.logger.WithFields(logrus.Fields{
|
||||
"route": route,
|
||||
"method": method,
|
||||
"body": body,
|
||||
}).WithError(err).Error("Failed to decode request to wails router")
|
||||
return WailsRequestRouterResponse{Body: nil, Error: err.Error()}
|
||||
}
|
||||
err = app.svc.AlbyOAuthSvc.SendPayment(ctx, payRequest.Invoice)
|
||||
err = app.svc.albyOAuthSvc.SendPayment(ctx, payRequest.Invoice)
|
||||
if err != nil {
|
||||
return WailsRequestRouterResponse{Body: nil, Error: err.Error()}
|
||||
}
|
||||
|
|
@ -197,7 +200,7 @@ func (app *WailsApp) WailsRequestRouter(route string, method string, body string
|
|||
createAppRequest := &api.CreateAppRequest{}
|
||||
err := json.Unmarshal([]byte(body), createAppRequest)
|
||||
if err != nil {
|
||||
app.svc.Logger.WithFields(logrus.Fields{
|
||||
app.svc.logger.WithFields(logrus.Fields{
|
||||
"route": route,
|
||||
"method": method,
|
||||
"body": body,
|
||||
|
|
@ -214,7 +217,7 @@ func (app *WailsApp) WailsRequestRouter(route string, method string, body string
|
|||
resetRouterRequest := &api.ResetRouterRequest{}
|
||||
err := json.Unmarshal([]byte(body), resetRouterRequest)
|
||||
if err != nil {
|
||||
app.svc.Logger.WithFields(logrus.Fields{
|
||||
app.svc.logger.WithFields(logrus.Fields{
|
||||
"route": route,
|
||||
"method": method,
|
||||
"body": body,
|
||||
|
|
@ -222,7 +225,7 @@ func (app *WailsApp) WailsRequestRouter(route string, method string, body string
|
|||
return WailsRequestRouterResponse{Body: nil, Error: err.Error()}
|
||||
}
|
||||
|
||||
err = app.api.ResetRouter(resetRouterRequest.Key, true)
|
||||
err = app.api.ResetRouter(resetRouterRequest.Key)
|
||||
if err != nil {
|
||||
return WailsRequestRouterResponse{Body: nil, Error: err.Error()}
|
||||
}
|
||||
|
|
@ -248,7 +251,7 @@ func (app *WailsApp) WailsRequestRouter(route string, method string, body string
|
|||
openChannelRequest := &api.OpenChannelRequest{}
|
||||
err := json.Unmarshal([]byte(body), openChannelRequest)
|
||||
if err != nil {
|
||||
app.svc.Logger.WithFields(logrus.Fields{
|
||||
app.svc.logger.WithFields(logrus.Fields{
|
||||
"route": route,
|
||||
"method": method,
|
||||
"body": body,
|
||||
|
|
@ -289,7 +292,7 @@ func (app *WailsApp) WailsRequestRouter(route string, method string, body string
|
|||
redeemOnchainFundsRequest := &api.RedeemOnchainFundsRequest{}
|
||||
err := json.Unmarshal([]byte(body), redeemOnchainFundsRequest)
|
||||
if err != nil {
|
||||
app.svc.Logger.WithFields(logrus.Fields{
|
||||
app.svc.logger.WithFields(logrus.Fields{
|
||||
"route": route,
|
||||
"method": method,
|
||||
"body": body,
|
||||
|
|
@ -310,6 +313,15 @@ func (app *WailsApp) WailsRequestRouter(route string, method string, body string
|
|||
return WailsRequestRouterResponse{Body: nil, Error: err.Error()}
|
||||
}
|
||||
return WailsRequestRouterResponse{Body: *signMessageResponse, Error: ""}
|
||||
// TODO: review naming
|
||||
case "/api/instant-channel-invoices":
|
||||
newInstantChannelRequest := &lsp.NewInstantChannelInvoiceRequest{}
|
||||
err := json.Unmarshal([]byte(body), newInstantChannelRequest)
|
||||
newInstantChannelResponseResponse, err := app.api.GetLSPService().NewInstantChannelInvoice(ctx, newInstantChannelRequest)
|
||||
if err != nil {
|
||||
return WailsRequestRouterResponse{Body: nil, Error: err.Error()}
|
||||
}
|
||||
return WailsRequestRouterResponse{Body: *newInstantChannelResponseResponse, Error: ""}
|
||||
case "/api/peers":
|
||||
switch method {
|
||||
case "GET":
|
||||
|
|
@ -322,7 +334,7 @@ func (app *WailsApp) WailsRequestRouter(route string, method string, body string
|
|||
connectPeerRequest := &api.ConnectPeerRequest{}
|
||||
err := json.Unmarshal([]byte(body), connectPeerRequest)
|
||||
if err != nil {
|
||||
app.svc.Logger.WithFields(logrus.Fields{
|
||||
app.svc.logger.WithFields(logrus.Fields{
|
||||
"route": route,
|
||||
"method": method,
|
||||
"body": body,
|
||||
|
|
@ -356,7 +368,7 @@ func (app *WailsApp) WailsRequestRouter(route string, method string, body string
|
|||
res := WailsRequestRouterResponse{Body: *infoResponse, Error: ""}
|
||||
return res
|
||||
case "/api/alby/link-account":
|
||||
err := app.svc.AlbyOAuthSvc.LinkAccount(ctx)
|
||||
err := app.svc.albyOAuthSvc.LinkAccount(ctx)
|
||||
if err != nil {
|
||||
return WailsRequestRouterResponse{Body: nil, Error: err.Error()}
|
||||
}
|
||||
|
|
@ -370,7 +382,7 @@ func (app *WailsApp) WailsRequestRouter(route string, method string, body string
|
|||
backupReminderRequest := &api.BackupReminderRequest{}
|
||||
err := json.Unmarshal([]byte(body), backupReminderRequest)
|
||||
if err != nil {
|
||||
app.svc.Logger.WithFields(logrus.Fields{
|
||||
app.svc.logger.WithFields(logrus.Fields{
|
||||
"route": route,
|
||||
"method": method,
|
||||
"body": body,
|
||||
|
|
@ -380,7 +392,7 @@ func (app *WailsApp) WailsRequestRouter(route string, method string, body string
|
|||
|
||||
err = app.api.SetNextBackupReminder(backupReminderRequest)
|
||||
if err != nil {
|
||||
app.svc.Logger.WithFields(logrus.Fields{
|
||||
app.svc.logger.WithFields(logrus.Fields{
|
||||
"route": route,
|
||||
"method": method,
|
||||
"body": body,
|
||||
|
|
@ -392,7 +404,7 @@ func (app *WailsApp) WailsRequestRouter(route string, method string, body string
|
|||
changeUnlockPasswordRequest := &api.ChangeUnlockPasswordRequest{}
|
||||
err := json.Unmarshal([]byte(body), changeUnlockPasswordRequest)
|
||||
if err != nil {
|
||||
app.svc.Logger.WithFields(logrus.Fields{
|
||||
app.svc.logger.WithFields(logrus.Fields{
|
||||
"route": route,
|
||||
"method": method,
|
||||
"body": body,
|
||||
|
|
@ -402,7 +414,7 @@ func (app *WailsApp) WailsRequestRouter(route string, method string, body string
|
|||
|
||||
err = app.api.ChangeUnlockPassword(changeUnlockPasswordRequest)
|
||||
if err != nil {
|
||||
app.svc.Logger.WithFields(logrus.Fields{
|
||||
app.svc.logger.WithFields(logrus.Fields{
|
||||
"route": route,
|
||||
"method": method,
|
||||
"body": body,
|
||||
|
|
@ -414,7 +426,7 @@ func (app *WailsApp) WailsRequestRouter(route string, method string, body string
|
|||
startRequest := &api.StartRequest{}
|
||||
err := json.Unmarshal([]byte(body), startRequest)
|
||||
if err != nil {
|
||||
app.svc.Logger.WithFields(logrus.Fields{
|
||||
app.svc.logger.WithFields(logrus.Fields{
|
||||
"route": route,
|
||||
"method": method,
|
||||
"body": body,
|
||||
|
|
@ -423,7 +435,7 @@ func (app *WailsApp) WailsRequestRouter(route string, method string, body string
|
|||
}
|
||||
err = app.api.Start(startRequest)
|
||||
if err != nil {
|
||||
app.svc.Logger.WithFields(logrus.Fields{
|
||||
app.svc.logger.WithFields(logrus.Fields{
|
||||
"route": route,
|
||||
"method": method,
|
||||
"body": body,
|
||||
|
|
@ -437,7 +449,7 @@ func (app *WailsApp) WailsRequestRouter(route string, method string, body string
|
|||
setupRequest := &api.SetupRequest{}
|
||||
err := json.Unmarshal([]byte(body), setupRequest)
|
||||
if err != nil {
|
||||
app.svc.Logger.WithFields(logrus.Fields{
|
||||
app.svc.logger.WithFields(logrus.Fields{
|
||||
"route": route,
|
||||
"method": method,
|
||||
"body": body,
|
||||
|
|
@ -446,7 +458,7 @@ func (app *WailsApp) WailsRequestRouter(route string, method string, body string
|
|||
}
|
||||
err = app.api.Setup(ctx, setupRequest)
|
||||
if err != nil {
|
||||
app.svc.Logger.WithFields(logrus.Fields{
|
||||
app.svc.logger.WithFields(logrus.Fields{
|
||||
"route": route,
|
||||
"method": method,
|
||||
"body": body,
|
||||
|
|
@ -458,7 +470,7 @@ func (app *WailsApp) WailsRequestRouter(route string, method string, body string
|
|||
sendPaymentProbesRequest := &api.SendPaymentProbesRequest{}
|
||||
err := json.Unmarshal([]byte(body), sendPaymentProbesRequest)
|
||||
if err != nil {
|
||||
app.svc.Logger.WithFields(logrus.Fields{
|
||||
app.svc.logger.WithFields(logrus.Fields{
|
||||
"route": route,
|
||||
"method": method,
|
||||
"body": body,
|
||||
|
|
@ -467,7 +479,7 @@ func (app *WailsApp) WailsRequestRouter(route string, method string, body string
|
|||
}
|
||||
sendPaymentProbesResponse, err := app.api.SendPaymentProbes(ctx, sendPaymentProbesRequest)
|
||||
if err != nil {
|
||||
app.svc.Logger.WithFields(logrus.Fields{
|
||||
app.svc.logger.WithFields(logrus.Fields{
|
||||
"route": route,
|
||||
"method": method,
|
||||
"body": body,
|
||||
|
|
@ -479,7 +491,7 @@ func (app *WailsApp) WailsRequestRouter(route string, method string, body string
|
|||
sendSpontaneousPaymentProbesRequest := &api.SendSpontaneousPaymentProbesRequest{}
|
||||
err := json.Unmarshal([]byte(body), sendSpontaneousPaymentProbesRequest)
|
||||
if err != nil {
|
||||
app.svc.Logger.WithFields(logrus.Fields{
|
||||
app.svc.logger.WithFields(logrus.Fields{
|
||||
"route": route,
|
||||
"method": method,
|
||||
"body": body,
|
||||
|
|
@ -488,7 +500,7 @@ func (app *WailsApp) WailsRequestRouter(route string, method string, body string
|
|||
}
|
||||
sendSpontaneousPaymentProbesResponse, err := app.api.SendSpontaneousPaymentProbes(ctx, sendSpontaneousPaymentProbesRequest)
|
||||
if err != nil {
|
||||
app.svc.Logger.WithFields(logrus.Fields{
|
||||
app.svc.logger.WithFields(logrus.Fields{
|
||||
"route": route,
|
||||
"method": method,
|
||||
"body": body,
|
||||
|
|
@ -500,7 +512,7 @@ func (app *WailsApp) WailsRequestRouter(route string, method string, body string
|
|||
backupRequest := &api.BasicBackupRequest{}
|
||||
err := json.Unmarshal([]byte(body), backupRequest)
|
||||
if err != nil {
|
||||
app.svc.Logger.WithFields(logrus.Fields{
|
||||
app.svc.logger.WithFields(logrus.Fields{
|
||||
"route": route,
|
||||
"method": method,
|
||||
"body": body,
|
||||
|
|
@ -513,7 +525,7 @@ func (app *WailsApp) WailsRequestRouter(route string, method string, body string
|
|||
DefaultFilename: "nwc.bkp",
|
||||
})
|
||||
if err != nil {
|
||||
app.svc.Logger.WithFields(logrus.Fields{
|
||||
app.svc.logger.WithFields(logrus.Fields{
|
||||
"route": route,
|
||||
"method": method,
|
||||
"body": body,
|
||||
|
|
@ -523,7 +535,7 @@ func (app *WailsApp) WailsRequestRouter(route string, method string, body string
|
|||
|
||||
backupFile, err := os.Create(saveFilePath)
|
||||
if err != nil {
|
||||
app.svc.Logger.WithFields(logrus.Fields{
|
||||
app.svc.logger.WithFields(logrus.Fields{
|
||||
"route": route,
|
||||
"method": method,
|
||||
"body": body,
|
||||
|
|
@ -533,14 +545,10 @@ func (app *WailsApp) WailsRequestRouter(route string, method string, body string
|
|||
|
||||
defer backupFile.Close()
|
||||
|
||||
backupReq := api.BasicBackupRequest{
|
||||
UnlockPassword: backupRequest.UnlockPassword,
|
||||
}
|
||||
|
||||
err = app.api.CreateBackup(&backupReq, backupFile)
|
||||
err = app.api.GetBackupService().CreateBackup(backupRequest.UnlockPassword, backupFile)
|
||||
|
||||
if err != nil {
|
||||
app.svc.Logger.WithFields(logrus.Fields{
|
||||
app.svc.logger.WithFields(logrus.Fields{
|
||||
"route": route,
|
||||
"method": method,
|
||||
"body": body,
|
||||
|
|
@ -552,7 +560,7 @@ func (app *WailsApp) WailsRequestRouter(route string, method string, body string
|
|||
restoreRequest := &api.BasicRestoreWailsRequest{}
|
||||
err := json.Unmarshal([]byte(body), restoreRequest)
|
||||
if err != nil {
|
||||
app.svc.Logger.WithFields(logrus.Fields{
|
||||
app.svc.logger.WithFields(logrus.Fields{
|
||||
"route": route,
|
||||
"method": method,
|
||||
"body": body,
|
||||
|
|
@ -565,7 +573,7 @@ func (app *WailsApp) WailsRequestRouter(route string, method string, body string
|
|||
DefaultFilename: "nwc.bkp",
|
||||
})
|
||||
if err != nil {
|
||||
app.svc.Logger.WithFields(logrus.Fields{
|
||||
app.svc.logger.WithFields(logrus.Fields{
|
||||
"route": route,
|
||||
"method": method,
|
||||
"body": body,
|
||||
|
|
@ -575,7 +583,7 @@ func (app *WailsApp) WailsRequestRouter(route string, method string, body string
|
|||
|
||||
backupFile, err := os.Open(backupFilePath)
|
||||
if err != nil {
|
||||
app.svc.Logger.WithFields(logrus.Fields{
|
||||
app.svc.logger.WithFields(logrus.Fields{
|
||||
"route": route,
|
||||
"method": method,
|
||||
"body": body,
|
||||
|
|
@ -585,9 +593,9 @@ func (app *WailsApp) WailsRequestRouter(route string, method string, body string
|
|||
|
||||
defer backupFile.Close()
|
||||
|
||||
err = app.api.RestoreBackup(restoreRequest.UnlockPassword, backupFile)
|
||||
err = app.api.GetBackupService().RestoreBackup(restoreRequest.UnlockPassword, backupFile)
|
||||
if err != nil {
|
||||
app.svc.Logger.WithFields(logrus.Fields{
|
||||
app.svc.logger.WithFields(logrus.Fields{
|
||||
"route": route,
|
||||
"method": method,
|
||||
"body": body,
|
||||
|
|
@ -605,7 +613,7 @@ func (app *WailsApp) WailsRequestRouter(route string, method string, body string
|
|||
getLogOutputRequest := &api.GetLogOutputRequest{}
|
||||
err := json.Unmarshal([]byte(body), getLogOutputRequest)
|
||||
if err != nil {
|
||||
app.svc.Logger.WithFields(logrus.Fields{
|
||||
app.svc.logger.WithFields(logrus.Fields{
|
||||
"route": route,
|
||||
"method": method,
|
||||
"body": body,
|
||||
|
|
@ -614,7 +622,7 @@ func (app *WailsApp) WailsRequestRouter(route string, method string, body string
|
|||
}
|
||||
logOutputResponse, err := app.api.GetLogOutput(ctx, logType, getLogOutputRequest)
|
||||
if err != nil {
|
||||
app.svc.Logger.WithFields(logrus.Fields{
|
||||
app.svc.logger.WithFields(logrus.Fields{
|
||||
"route": route,
|
||||
"method": method,
|
||||
"body": body,
|
||||
|
|
@ -624,7 +632,7 @@ func (app *WailsApp) WailsRequestRouter(route string, method string, body string
|
|||
return WailsRequestRouterResponse{Body: logOutputResponse, Error: ""}
|
||||
}
|
||||
|
||||
app.svc.Logger.WithFields(logrus.Fields{
|
||||
app.svc.logger.WithFields(logrus.Fields{
|
||||
"route": route,
|
||||
"method": method,
|
||||
}).Error("Unhandled route")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue