lightningtip/backends/lnd.go

126 lines
2.5 KiB
Go

package backends
import (
"context"
"encoding/hex"
"errors"
"github.com/donovanhide/eventsource"
"github.com/lightningnetwork/lnd/lnrpc"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/metadata"
"io"
"io/ioutil"
)
type LND struct {
GRPCHost string `long:"grpchost" Description:"Host of the gRPC interface of LND"`
CertFile string `long:"certfile" Description:"TLS certificate for the LND gRPC and REST services"`
MacaroonFile string `long:"macaroonfile" Description:"Admin macaroon file for authentication. Set to an empty string for no macaroon"`
client lnrpc.LightningClient
ctx context.Context
}
func (lnd *LND) Connect() error {
creds, err := credentials.NewClientTLSFromFile(lnd.CertFile, "")
if err != nil {
log.Error("Failed to read certificate for LND gRPC")
return err
}
con, err := grpc.Dial(lnd.GRPCHost, grpc.WithTransportCredentials(creds))
if err != nil {
log.Error("Failed to connect to LND gRPC server")
return err
}
lnd.ctx = context.Background()
if lnd.MacaroonFile != "" {
macaroon, err := getMacaroon(lnd.MacaroonFile)
if macaroon == nil && err != nil {
log.Error("Failed to read admin macaroon file of LND")
}
lnd.ctx = metadata.NewOutgoingContext(lnd.ctx, macaroon)
}
lnd.client = lnrpc.NewLightningClient(con)
return err
}
func (lnd *LND) GetInvoice(message string, amount int64, expiry int64) (invoice string, err error) {
var response *lnrpc.AddInvoiceResponse
response, err = lnd.client.AddInvoice(lnd.ctx, &lnrpc.Invoice{
Memo: message,
Value: amount,
Expiry: expiry,
})
if err != nil {
return "", err
}
return response.PaymentRequest, err
}
func (lnd *LND) SubscribeInvoices(callback PublishInvoiceSettled, eventSrv *eventsource.Server) error {
stream, err := lnd.client.SubscribeInvoices(lnd.ctx, &lnrpc.InvoiceSubscription{})
if err != nil {
return err
}
wait := make(chan struct{})
go func() {
for {
invoice, streamErr := stream.Recv()
if streamErr == io.EOF {
err = errors.New("lost connection to LND gRPC")
close(wait)
return
}
if streamErr != nil {
err = streamErr
close(wait)
return
}
if invoice.Settled {
callback(invoice.PaymentRequest, eventSrv)
}
}
}()
<-wait
return err
}
func getMacaroon(macaroonFile string) (macaroon metadata.MD, err error) {
data, err := ioutil.ReadFile(macaroonFile)
if err == nil {
macaroon = metadata.Pairs("macaroon", hex.EncodeToString(data))
}
return macaroon, err
}