mirror of
https://github.com/getAlby/hub.git
synced 2026-08-13 12:33:39 +02:00
feat: rebalance through node pubkey (#1731)
* feat: rebalance through node pubkey (WIP) * fix: check if remote channel policy exists
This commit is contained in:
parent
3a4d04bc20
commit
a294e1b169
13 changed files with 101 additions and 30 deletions
|
|
@ -26,7 +26,7 @@ func (api *api) RebalanceChannel(ctx context.Context, rebalanceChannelRequest *R
|
|||
"receive_through": rebalanceChannelRequest.ReceiveThroughNodePubkey,
|
||||
}
|
||||
|
||||
receiveInvoice, err := api.svc.GetTransactionsService().MakeInvoice(ctx, rebalanceChannelRequest.AmountSat*1000, "Alby Hub Rebalance through "+rebalanceChannelRequest.ReceiveThroughNodePubkey, "", 0, receiveMetadata, api.svc.GetLNClient(), nil, nil)
|
||||
receiveInvoice, err := api.svc.GetTransactionsService().MakeInvoice(ctx, rebalanceChannelRequest.AmountSat*1000, "Alby Hub Rebalance through "+rebalanceChannelRequest.ReceiveThroughNodePubkey, "", 0, receiveMetadata, api.svc.GetLNClient(), nil, nil, &rebalanceChannelRequest.ReceiveThroughNodePubkey)
|
||||
if err != nil {
|
||||
logger.Logger.WithError(err).Error("failed to generate rebalance receive invoice")
|
||||
return nil, err
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ func (api *api) CreateInvoice(ctx context.Context, amount uint64, description st
|
|||
if api.svc.GetLNClient() == nil {
|
||||
return nil, errors.New("LNClient not started")
|
||||
}
|
||||
transaction, err := api.svc.GetTransactionsService().MakeInvoice(ctx, amount, description, "", 0, nil, api.svc.GetLNClient(), nil, nil)
|
||||
transaction, err := api.svc.GetTransactionsService().MakeInvoice(ctx, amount, description, "", 0, nil, api.svc.GetLNClient(), nil, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -144,7 +144,7 @@ func (api *api) Transfer(ctx context.Context, fromAppId *uint, toAppId *uint, am
|
|||
}
|
||||
}
|
||||
|
||||
transaction, err := api.svc.GetTransactionsService().MakeInvoice(ctx, amountMsat, "transfer", "", 0, nil, api.svc.GetLNClient(), toAppId, nil)
|
||||
transaction, err := api.svc.GetTransactionsService().MakeInvoice(ctx, amountMsat, "transfer", "", 0, nil, api.svc.GetLNClient(), toAppId, nil, nil)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ func (cs *CashuService) SendKeysend(amount uint64, destination string, custom_re
|
|||
return nil, errors.New("keysend not supported")
|
||||
}
|
||||
|
||||
func (cs *CashuService) MakeInvoice(ctx context.Context, amount int64, description string, descriptionHash string, expiry int64) (transaction *lnclient.Transaction, err error) {
|
||||
func (cs *CashuService) MakeInvoice(ctx context.Context, amount int64, description string, descriptionHash string, expiry int64, throughNodePubkey *string) (transaction *lnclient.Transaction, err error) {
|
||||
// TODO: support expiry
|
||||
if expiry == 0 {
|
||||
expiry = lnclient.DEFAULT_INVOICE_EXPIRY
|
||||
|
|
|
|||
|
|
@ -717,7 +717,7 @@ func (ls *LDKService) getMaxSpendable() uint64 {
|
|||
return spendable
|
||||
}
|
||||
|
||||
func (ls *LDKService) MakeInvoice(ctx context.Context, amount int64, description string, descriptionHash string, expiry int64) (transaction *lnclient.Transaction, err error) {
|
||||
func (ls *LDKService) MakeInvoice(ctx context.Context, amount int64, description string, descriptionHash string, expiry int64, throughNodePubkey *string) (transaction *lnclient.Transaction, err error) {
|
||||
|
||||
if time.Duration(expiry)*time.Second > maxInvoiceExpiry {
|
||||
return nil, errors.New("expiry is too long")
|
||||
|
|
|
|||
|
|
@ -599,7 +599,7 @@ func (svc *LNDService) getPaymentResult(stream routerrpc.Router_SendPaymentV2Cli
|
|||
}
|
||||
}
|
||||
|
||||
func (svc *LNDService) MakeInvoice(ctx context.Context, amount int64, description string, descriptionHash string, expiry int64) (transaction *lnclient.Transaction, err error) {
|
||||
func (svc *LNDService) MakeInvoice(ctx context.Context, amount int64, description string, descriptionHash string, expiry int64, throughNodePubkey *string) (transaction *lnclient.Transaction, err error) {
|
||||
var descriptionHashBytes []byte
|
||||
|
||||
if descriptionHash != "" {
|
||||
|
|
@ -628,6 +628,75 @@ func (svc *LNDService) MakeInvoice(ctx context.Context, amount int64, descriptio
|
|||
for _, channel := range channels {
|
||||
if channel.Active && channel.Public {
|
||||
hasPublicChannels = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
var hints []*lnrpc.RouteHint
|
||||
if !hasPublicChannels && throughNodePubkey != nil {
|
||||
channelsRes, err := svc.client.ListChannels(ctx, &lnrpc.ListChannelsRequest{
|
||||
PrivateOnly: true,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, channel := range channelsRes.Channels {
|
||||
if channel.RemotePubkey != *throughNodePubkey {
|
||||
continue
|
||||
}
|
||||
|
||||
chanInfo, err := svc.client.GetChanInfo(ctx, &lnrpc.ChanInfoRequest{
|
||||
ChanId: channel.ChanId,
|
||||
})
|
||||
if err != nil {
|
||||
logger.Logger.WithFields(logrus.Fields{
|
||||
"channel_id": channel.ChanId,
|
||||
}).WithError(err).Error("Unable to get channel info")
|
||||
continue
|
||||
}
|
||||
|
||||
var remotePolicy *lnrpc.RoutingPolicy
|
||||
if chanInfo.Node1Pub == channel.RemotePubkey {
|
||||
remotePolicy = chanInfo.Node1Policy
|
||||
} else {
|
||||
remotePolicy = chanInfo.Node2Policy
|
||||
}
|
||||
|
||||
if remotePolicy == nil {
|
||||
logger.Logger.WithFields(logrus.Fields{
|
||||
"channel_id": channel.ChanId,
|
||||
}).WithError(err).Error("Remote channel policy does not exist")
|
||||
continue
|
||||
}
|
||||
|
||||
channelId := chanInfo.ChannelId
|
||||
if channel.PeerScidAlias != 0 {
|
||||
channelId = channel.PeerScidAlias
|
||||
}
|
||||
|
||||
hint := &lnrpc.RouteHint{
|
||||
HopHints: []*lnrpc.HopHint{
|
||||
{
|
||||
NodeId: channel.RemotePubkey,
|
||||
ChanId: channelId,
|
||||
FeeBaseMsat: uint32(remotePolicy.FeeBaseMsat),
|
||||
FeeProportionalMillionths: uint32(remotePolicy.FeeRateMilliMsat),
|
||||
CltvExpiryDelta: remotePolicy.TimeLockDelta,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
hints = append(hints, hint)
|
||||
if len(hints) == 3 {
|
||||
// limit to 3 channels
|
||||
// NOTE: there is no check that the channels are online or have enough receiving capacity.
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if len(hints) == 0 {
|
||||
return nil, errors.New("no channel found for given throughNodePubkey")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -636,6 +705,7 @@ func (svc *LNDService) MakeInvoice(ctx context.Context, amount int64, descriptio
|
|||
Memo: description,
|
||||
DescriptionHash: descriptionHashBytes,
|
||||
Expiry: expiry,
|
||||
RouteHints: hints,
|
||||
Private: !hasPublicChannels, // use private channel hints in the invoice
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ type LNClient interface {
|
|||
SendKeysend(amount uint64, destination string, customRecords []TLVRecord, preimage string) (*PayKeysendResponse, error)
|
||||
GetPubkey() string
|
||||
GetInfo(ctx context.Context) (info *NodeInfo, err error)
|
||||
MakeInvoice(ctx context.Context, amount int64, description string, descriptionHash string, expiry int64) (transaction *Transaction, err error)
|
||||
MakeInvoice(ctx context.Context, amount int64, description string, descriptionHash string, expiry int64, throughNodePubkey *string) (transaction *Transaction, err error)
|
||||
MakeHoldInvoice(ctx context.Context, amount int64, description string, descriptionHash string, expiry int64, paymentHash string) (transaction *Transaction, err error)
|
||||
SettleHoldInvoice(ctx context.Context, preimage string) (err error)
|
||||
CancelHoldInvoice(ctx context.Context, paymentHash string) (err error)
|
||||
|
|
|
|||
|
|
@ -276,7 +276,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 *lnclient.Transaction, err error) {
|
||||
func (svc *PhoenixService) MakeInvoice(ctx context.Context, amount int64, description string, descriptionHash string, expiry int64, throughNodePubkey *string) (transaction *lnclient.Transaction, err error) {
|
||||
// TODO: support expiry
|
||||
if expiry == 0 {
|
||||
expiry = lnclient.DEFAULT_INVOICE_EXPIRY
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ func (controller *nip47Controller) HandleMakeInvoiceEvent(ctx context.Context, n
|
|||
|
||||
expiry := makeInvoiceParams.Expiry
|
||||
|
||||
transaction, err := controller.transactionsService.MakeInvoice(ctx, makeInvoiceParams.Amount, makeInvoiceParams.Description, makeInvoiceParams.DescriptionHash, expiry, makeInvoiceParams.Metadata, controller.lnClient, &appId, &requestEventId)
|
||||
transaction, err := controller.transactionsService.MakeInvoice(ctx, makeInvoiceParams.Amount, makeInvoiceParams.Description, makeInvoiceParams.DescriptionHash, expiry, makeInvoiceParams.Metadata, controller.lnClient, &appId, &requestEventId, nil)
|
||||
if err != nil {
|
||||
logger.Logger.WithFields(logrus.Fields{
|
||||
"request_event_id": requestEventId,
|
||||
|
|
|
|||
|
|
@ -400,7 +400,7 @@ func (svc *swapsService) SwapOut(amount uint64, destination string, useExactRece
|
|||
|
||||
func (svc *swapsService) SwapIn(amount uint64, autoSwap bool) (*SwapResponse, error) {
|
||||
amountMSat := amount * 1000
|
||||
invoice, err := svc.transactionsService.MakeInvoice(svc.ctx, amountMSat, "On-chain to lightning swap", "", 0, nil, svc.lnClient, nil, nil)
|
||||
invoice, err := svc.transactionsService.MakeInvoice(svc.ctx, amountMSat, "On-chain to lightning swap", "", 0, nil, svc.lnClient, nil, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ 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 *lnclient.Transaction, err error) {
|
||||
func (mln *MockLn) MakeInvoice(ctx context.Context, amount int64, description string, descriptionHash string, expiry int64, throughNodePubkey *string) (transaction *lnclient.Transaction, err error) {
|
||||
return MockLNClientTransaction, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1323,8 +1323,8 @@ func (_c *MockLNClient_MakeHoldInvoice_Call) RunAndReturn(run func(ctx context.C
|
|||
}
|
||||
|
||||
// MakeInvoice provides a mock function for the type MockLNClient
|
||||
func (_mock *MockLNClient) MakeInvoice(ctx context.Context, amount int64, description string, descriptionHash string, expiry int64) (*lnclient.Transaction, error) {
|
||||
ret := _mock.Called(ctx, amount, description, descriptionHash, expiry)
|
||||
func (_mock *MockLNClient) MakeInvoice(ctx context.Context, amount int64, description string, descriptionHash string, expiry int64, throughNodePubkey *string) (*lnclient.Transaction, error) {
|
||||
ret := _mock.Called(ctx, amount, description, descriptionHash, expiry, throughNodePubkey)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for MakeInvoice")
|
||||
|
|
@ -1332,18 +1332,18 @@ func (_mock *MockLNClient) MakeInvoice(ctx context.Context, amount int64, descri
|
|||
|
||||
var r0 *lnclient.Transaction
|
||||
var r1 error
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, int64, string, string, int64) (*lnclient.Transaction, error)); ok {
|
||||
return returnFunc(ctx, amount, description, descriptionHash, expiry)
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, int64, string, string, int64, *string) (*lnclient.Transaction, error)); ok {
|
||||
return returnFunc(ctx, amount, description, descriptionHash, expiry, throughNodePubkey)
|
||||
}
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, int64, string, string, int64) *lnclient.Transaction); ok {
|
||||
r0 = returnFunc(ctx, amount, description, descriptionHash, expiry)
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, int64, string, string, int64, *string) *lnclient.Transaction); ok {
|
||||
r0 = returnFunc(ctx, amount, description, descriptionHash, expiry, throughNodePubkey)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*lnclient.Transaction)
|
||||
}
|
||||
}
|
||||
if returnFunc, ok := ret.Get(1).(func(context.Context, int64, string, string, int64) error); ok {
|
||||
r1 = returnFunc(ctx, amount, description, descriptionHash, expiry)
|
||||
if returnFunc, ok := ret.Get(1).(func(context.Context, int64, string, string, int64, *string) error); ok {
|
||||
r1 = returnFunc(ctx, amount, description, descriptionHash, expiry, throughNodePubkey)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
|
@ -1361,13 +1361,14 @@ type MockLNClient_MakeInvoice_Call struct {
|
|||
// - description
|
||||
// - descriptionHash
|
||||
// - expiry
|
||||
func (_e *MockLNClient_Expecter) MakeInvoice(ctx interface{}, amount interface{}, description interface{}, descriptionHash interface{}, expiry interface{}) *MockLNClient_MakeInvoice_Call {
|
||||
return &MockLNClient_MakeInvoice_Call{Call: _e.mock.On("MakeInvoice", ctx, amount, description, descriptionHash, expiry)}
|
||||
// - throughNodePubkey
|
||||
func (_e *MockLNClient_Expecter) MakeInvoice(ctx interface{}, amount interface{}, description interface{}, descriptionHash interface{}, expiry interface{}, throughNodePubkey interface{}) *MockLNClient_MakeInvoice_Call {
|
||||
return &MockLNClient_MakeInvoice_Call{Call: _e.mock.On("MakeInvoice", ctx, amount, description, descriptionHash, expiry, throughNodePubkey)}
|
||||
}
|
||||
|
||||
func (_c *MockLNClient_MakeInvoice_Call) Run(run func(ctx context.Context, amount int64, description string, descriptionHash string, expiry int64)) *MockLNClient_MakeInvoice_Call {
|
||||
func (_c *MockLNClient_MakeInvoice_Call) Run(run func(ctx context.Context, amount int64, description string, descriptionHash string, expiry int64, throughNodePubkey *string)) *MockLNClient_MakeInvoice_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(context.Context), args[1].(int64), args[2].(string), args[3].(string), args[4].(int64))
|
||||
run(args[0].(context.Context), args[1].(int64), args[2].(string), args[3].(string), args[4].(int64), args[5].(*string))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
|
@ -1377,7 +1378,7 @@ func (_c *MockLNClient_MakeInvoice_Call) Return(transaction *lnclient.Transactio
|
|||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockLNClient_MakeInvoice_Call) RunAndReturn(run func(ctx context.Context, amount int64, description string, descriptionHash string, expiry int64) (*lnclient.Transaction, error)) *MockLNClient_MakeInvoice_Call {
|
||||
func (_c *MockLNClient_MakeInvoice_Call) RunAndReturn(run func(ctx context.Context, amount int64, description string, descriptionHash string, expiry int64, throughNodePubkey *string) (*lnclient.Transaction, error)) *MockLNClient_MakeInvoice_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ func TestMakeInvoice_NoApp(t *testing.T) {
|
|||
txMetadata["randomkey"] = strings.Repeat("a", constants.INVOICE_METADATA_MAX_LENGTH-16) // json encoding adds 16 characters - {"randomkey":""}
|
||||
|
||||
transactionsService := NewTransactionsService(svc.DB, svc.EventPublisher)
|
||||
transaction, err := transactionsService.MakeInvoice(ctx, 1234, "Hello world", "", 0, txMetadata, svc.LNClient, nil, nil)
|
||||
transaction, err := transactionsService.MakeInvoice(ctx, 1234, "Hello world", "", 0, txMetadata, svc.LNClient, nil, nil, nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
var metadata map[string]interface{}
|
||||
|
|
@ -50,7 +50,7 @@ func TestMakeInvoice_MetadataTooLarge(t *testing.T) {
|
|||
metadata["randomkey"] = strings.Repeat("a", constants.INVOICE_METADATA_MAX_LENGTH-15) // json encoding adds 16 characters
|
||||
|
||||
transactionsService := NewTransactionsService(svc.DB, svc.EventPublisher)
|
||||
transaction, err := transactionsService.MakeInvoice(ctx, 1234, "Hello world", "", 0, metadata, svc.LNClient, nil, nil)
|
||||
transaction, err := transactionsService.MakeInvoice(ctx, 1234, "Hello world", "", 0, metadata, svc.LNClient, nil, nil, nil)
|
||||
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, fmt.Sprintf("encoded invoice metadata provided is too large. Limit: %d Received: %d", constants.INVOICE_METADATA_MAX_LENGTH, constants.INVOICE_METADATA_MAX_LENGTH+1), err.Error())
|
||||
|
|
@ -72,7 +72,7 @@ func TestMakeInvoice_App(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
|
||||
transactionsService := NewTransactionsService(svc.DB, svc.EventPublisher)
|
||||
transaction, err := transactionsService.MakeInvoice(ctx, 1234, "Hello world", "", 0, nil, svc.LNClient, &app.ID, &dbRequestEvent.ID)
|
||||
transaction, err := transactionsService.MakeInvoice(ctx, 1234, "Hello world", "", 0, nil, svc.LNClient, &app.ID, &dbRequestEvent.ID, nil)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, uint64(tests.MockLNClientTransaction.Amount), transaction.AmountMsat)
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ type transactionsService struct {
|
|||
|
||||
type TransactionsService interface {
|
||||
events.EventSubscriber
|
||||
MakeInvoice(ctx context.Context, amount uint64, description string, descriptionHash string, expiry uint64, metadata map[string]interface{}, lnClient lnclient.LNClient, appId *uint, requestEventId *uint) (*Transaction, error)
|
||||
MakeInvoice(ctx context.Context, amount uint64, description string, descriptionHash string, expiry uint64, metadata map[string]interface{}, lnClient lnclient.LNClient, appId *uint, requestEventId *uint, throughNodePubkey *string) (*Transaction, error)
|
||||
LookupTransaction(ctx context.Context, paymentHash string, transactionType *string, lnClient lnclient.LNClient, appId *uint) (*Transaction, error)
|
||||
ListTransactions(ctx context.Context, from, until, limit, offset uint64, unpaidOutgoing bool, unpaidIncoming bool, transactionType *string, lnClient lnclient.LNClient, appId *uint, forceFilterByAppId bool) (transactions []Transaction, totalCount uint64, err error)
|
||||
SendPaymentSync(payReq string, amountMsat *uint64, metadata map[string]interface{}, lnClient lnclient.LNClient, appId *uint, requestEventId *uint) (*Transaction, error)
|
||||
|
|
@ -139,7 +139,7 @@ func NewTransactionsService(db *gorm.DB, eventPublisher events.EventPublisher) *
|
|||
}
|
||||
}
|
||||
|
||||
func (svc *transactionsService) MakeInvoice(ctx context.Context, amount uint64, description string, descriptionHash string, expiry uint64, metadata map[string]interface{}, lnClient lnclient.LNClient, appId *uint, requestEventId *uint) (*Transaction, error) {
|
||||
func (svc *transactionsService) MakeInvoice(ctx context.Context, amount uint64, description string, descriptionHash string, expiry uint64, metadata map[string]interface{}, lnClient lnclient.LNClient, appId *uint, requestEventId *uint, throughNodePubkey *string) (*Transaction, error) {
|
||||
logger.Logger.WithFields(logrus.Fields{
|
||||
"app_id": appId,
|
||||
"request_event_id": requestEventId,
|
||||
|
|
@ -173,7 +173,7 @@ func (svc *transactionsService) MakeInvoice(ctx context.Context, amount uint64,
|
|||
appId = &overwriteAppId
|
||||
}
|
||||
|
||||
lnClientTransaction, err := lnClient.MakeInvoice(ctx, int64(amount), description, descriptionHash, int64(expiry))
|
||||
lnClientTransaction, err := lnClient.MakeInvoice(ctx, int64(amount), description, descriptionHash, int64(expiry), throughNodePubkey)
|
||||
if err != nil {
|
||||
logger.Logger.WithError(err).Error("Failed to create transaction")
|
||||
return nil, err
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue