keysend: low-level work and instructions

This commit is contained in:
kiwiidb
2022-03-04 11:08:27 +01:00
parent 8e4646982d
commit 3d8ecd425f
6 changed files with 116 additions and 0 deletions

49
controllers/keysend.go Normal file
View 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")
}

View File

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

View File

@@ -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"`

View 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
}

View File

@@ -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{

View File

@@ -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
}