mirror of
https://github.com/getAlby/lndhub.go.git
synced 2026-02-23 13:54:25 +01:00
keysend: low-level work and instructions
This commit is contained in:
49
controllers/keysend.go
Normal file
49
controllers/keysend.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/getAlby/lndhub.go/lib"
|
||||
"github.com/getAlby/lndhub.go/lib/service"
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
// KeySendController : Pay invoice controller struct
|
||||
type KeySendController struct {
|
||||
svc *service.LndhubService
|
||||
}
|
||||
|
||||
func NewKeySendController(svc *service.LndhubService) *KeySendController {
|
||||
return &KeySendController{svc: svc}
|
||||
}
|
||||
|
||||
type KeySendRequestBody struct {
|
||||
Amount int64 `json:"amount" validate:"required"`
|
||||
Destination string `json:"destination" validate:"required"`
|
||||
Memo string `json:"memo" validate:"omitempty"`
|
||||
}
|
||||
|
||||
type KeySendResponseBody struct {
|
||||
RHash *lib.JavaScriptBuffer `json:"payment_hash,omitempty"`
|
||||
Amount int64 `json:"num_satoshis,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Destination string `json:"destination,omitempty"`
|
||||
DescriptionHashStr string `json:"description_hash,omitempty"`
|
||||
PaymentError string `json:"payment_error,omitempty"`
|
||||
PaymentPreimage *lib.JavaScriptBuffer `json:"payment_preimage,omitempty"`
|
||||
PaymentRoute *service.Route `json:"route,omitempty"`
|
||||
}
|
||||
|
||||
// KeySend : Pay invoice Controller
|
||||
func (controller *KeySendController) KeySend(c echo.Context) error {
|
||||
/*
|
||||
TODO: copy code from payinvoice.ctrl.go and modify where needed:
|
||||
- do not decode the payment request because there is no payment request.
|
||||
Instead, construct the lnrpc.PaymentRequest manually from the KeySendRequestBody.
|
||||
- add outgoing invoice: same as payinvoice, make sure to set keysend=true
|
||||
- do a balance check: same as payinvoice, in fact do this before doing anything else
|
||||
- call svc.PayInvoice : same as payinvoice as long as keysend=true in Invoice
|
||||
- response will be slightly different due to lack of payment request
|
||||
*/
|
||||
return fmt.Errorf("TODO")
|
||||
}
|
||||
1
db/migrations/20220304103000_keysend_invoice.up.sql
Normal file
1
db/migrations/20220304103000_keysend_invoice.up.sql
Normal file
@@ -0,0 +1 @@
|
||||
alter table invoices add column keysend boolean;
|
||||
@@ -21,6 +21,7 @@ type Invoice struct {
|
||||
RHash string `json:"r_hash"`
|
||||
Preimage string `json:"preimage" bun:",nullzero"`
|
||||
Internal bool `json:"internal" bun:",nullzero"`
|
||||
KeySend bool `json:"keysend" bun:",nullzero"`
|
||||
State string `json:"state" bun:",default:'initialized'"`
|
||||
ErrorMessage string `json:"error_message" bun:",nullzero"`
|
||||
AddIndex uint64 `json:"add_index" bun:",nullzero"`
|
||||
|
||||
12
integration_tests/keysend_test.go
Normal file
12
integration_tests/keysend_test.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package integration_tests
|
||||
|
||||
func (suite *PaymentTestSuite) TestKeysendPayment() {
|
||||
// destination pubkey strings:
|
||||
// simnet-lnd-2: 025c1d5d1b4c983cc6350fc2d756fbb59b4dc365e45e87f8e3afe07e24013e8220
|
||||
// simnet-lnd-3: 03c7092d076f799ab18806743634b4c9bb34e351bdebc91d5b35963f3dc63ec5aa
|
||||
// simnet-cln-1: 0242898f86064c2fd72de22059c947a83ba23e9d97aedeae7b6dba647123f1d71b
|
||||
// (put this in utils)
|
||||
// fund account, test making keysend payments to any of these nodes (lnd-2 and lnd-3 is fine)
|
||||
// test making a keysend payment to a destination that does not exist
|
||||
// test making a keysend payment with a memo that is waaaaaaay too long
|
||||
}
|
||||
@@ -98,6 +98,9 @@ func (svc *LndhubService) SendInternalPayment(ctx context.Context, invoice *mode
|
||||
}
|
||||
|
||||
func (svc *LndhubService) SendPaymentSync(ctx context.Context, invoice *models.Invoice) (SendPaymentResponse, error) {
|
||||
if invoice.KeySend {
|
||||
return svc.KeySendPaymentSync(ctx, invoice)
|
||||
}
|
||||
sendPaymentResponse := SendPaymentResponse{}
|
||||
// TODO: set dynamic fee limit
|
||||
feeLimit := lnrpc.FeeLimit{
|
||||
|
||||
@@ -2,10 +2,60 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
|
||||
"github.com/getAlby/lndhub.go/db/models"
|
||||
"github.com/lightningnetwork/lnd/lnrpc"
|
||||
)
|
||||
|
||||
//https://github.com/hsjoberg/blixt-wallet/blob/9fcc56a7dc25237bc14b85e6490adb9e044c009c/src/utils/constants.ts#L5
|
||||
const (
|
||||
KEYSEND_CUSTOM_RECORD = 5482373484
|
||||
TLV_WHATSAT_MESSAGE = 34349334
|
||||
TLV_RECORD_NAME = 128100
|
||||
)
|
||||
|
||||
func (svc *LndhubService) GetInfo(ctx context.Context) (*lnrpc.GetInfoResponse, error) {
|
||||
return svc.LndClient.GetInfo(ctx, &lnrpc.GetInfoRequest{})
|
||||
}
|
||||
|
||||
func (svc *LndhubService) KeySendPaymentSync(ctx context.Context, invoice *models.Invoice) (result SendPaymentResponse, err error) {
|
||||
sendPaymentResponse := SendPaymentResponse{}
|
||||
// TODO: set dynamic fee limit
|
||||
feeLimit := lnrpc.FeeLimit{
|
||||
Limit: &lnrpc.FeeLimit_Fixed{
|
||||
Fixed: 300,
|
||||
},
|
||||
}
|
||||
preImage := makePreimageHex()
|
||||
// Prepare the LNRPC call
|
||||
//See: https://github.com/hsjoberg/blixt-wallet/blob/9fcc56a7dc25237bc14b85e6490adb9e044c009c/src/lndmobile/index.ts#L251-L270
|
||||
sendPaymentRequest := lnrpc.SendRequest{
|
||||
Dest: []byte(invoice.DestinationPubkeyHex),
|
||||
Amt: invoice.Amount,
|
||||
FeeLimit: &feeLimit,
|
||||
DestFeatures: []lnrpc.FeatureBit{lnrpc.FeatureBit_TLV_ONION_REQ},
|
||||
DestCustomRecords: map[uint64][]byte{KEYSEND_CUSTOM_RECORD: preImage, TLV_WHATSAT_MESSAGE: []byte(invoice.Memo)},
|
||||
}
|
||||
|
||||
// Execute the payment
|
||||
sendPaymentResult, err := svc.LndClient.SendPaymentSync(ctx, &sendPaymentRequest)
|
||||
if err != nil {
|
||||
return sendPaymentResponse, err
|
||||
}
|
||||
|
||||
// If there was a payment error we return an error
|
||||
if sendPaymentResult.GetPaymentError() != "" || sendPaymentResult.GetPaymentPreimage() == nil {
|
||||
return sendPaymentResponse, errors.New(sendPaymentResult.GetPaymentError())
|
||||
}
|
||||
|
||||
preimage := sendPaymentResult.GetPaymentPreimage()
|
||||
sendPaymentResponse.PaymentPreimage = preimage
|
||||
sendPaymentResponse.PaymentPreimageStr = hex.EncodeToString(preimage[:])
|
||||
paymentHash := sendPaymentResult.GetPaymentHash()
|
||||
sendPaymentResponse.PaymentHash = paymentHash
|
||||
sendPaymentResponse.PaymentHashStr = hex.EncodeToString(paymentHash[:])
|
||||
sendPaymentResponse.PaymentRoute = &Route{TotalAmt: sendPaymentResult.PaymentRoute.TotalAmt, TotalFees: sendPaymentResult.PaymentRoute.TotalFees}
|
||||
return sendPaymentResponse, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user