finished LND implementation
This commit is contained in:
parent
cc124317ea
commit
f7e60168b4
|
@ -3,10 +3,13 @@ package backends
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
"github.com/lightningnetwork/lnd/lnrpc"
|
"github.com/lightningnetwork/lnd/lnrpc"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/credentials"
|
"google.golang.org/grpc/credentials"
|
||||||
"google.golang.org/grpc/metadata"
|
"google.golang.org/grpc/metadata"
|
||||||
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -76,3 +79,41 @@ func (lnd *LND) GetInvoice(description string, value int64, expiry int64) (invoi
|
||||||
|
|
||||||
return response.PaymentRequest, err
|
return response.PaymentRequest, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (lnd *LND) SubscribeInvoices() error {
|
||||||
|
stream, err := lnd.client.SubscribeInvoices(lnd.ctx, &lnrpc.InvoiceSubscription{})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
wait := make(chan struct{})
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
in, 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
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(in)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
<-wait
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
24
config.go
24
config.go
|
@ -10,12 +10,20 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO: add option to show URI of Lightning node
|
||||||
|
|
||||||
const (
|
const (
|
||||||
defaultConfigFile = "lightningTip.conf"
|
defaultConfigFile = "lightningTip.conf"
|
||||||
|
|
||||||
defaultLogFile = "lightningTip.log"
|
defaultLogFile = "lightningTip.log"
|
||||||
defaultLogLevel = "debug"
|
defaultLogLevel = "debug"
|
||||||
|
|
||||||
|
defaultRESTHost = "localhost:8081"
|
||||||
|
|
||||||
|
defaultTipMessage = "A generous tip"
|
||||||
|
defaultTipExpiry = 3600
|
||||||
|
defaultTipValue = 100
|
||||||
|
|
||||||
defaultLndRPCHost = "localhost:10009"
|
defaultLndRPCHost = "localhost:10009"
|
||||||
defaultLndCertFile = "tls.cert"
|
defaultLndCertFile = "tls.cert"
|
||||||
defaultMacaroonFile = "admin.macaroon"
|
defaultMacaroonFile = "admin.macaroon"
|
||||||
|
@ -27,13 +35,18 @@ type config struct {
|
||||||
LogFile string `long:"logfile" Description:"Log file location"`
|
LogFile string `long:"logfile" Description:"Log file location"`
|
||||||
LogLevel string `long:"loglevel" Description:"Log level: debug, info, warning, error"`
|
LogLevel string `long:"loglevel" Description:"Log level: debug, info, warning, error"`
|
||||||
|
|
||||||
|
RESTHost string `long:"resthost" Description:"Host for the rest interface of LightningTip"`
|
||||||
|
|
||||||
|
TipMessage string `long:"tipmessage" Description:"Message embedded in the payment request"`
|
||||||
|
TipExpiry int64 `long:"tipexpiry" Description:"Expiry time in seconds"`
|
||||||
|
DefaultTipValue int64 `long:"defaulttipvalue" Description:"The default value of a tip in satoshis"`
|
||||||
|
|
||||||
LND *backends.LND `group:"LND" namespace:"lnd"`
|
LND *backends.LND `group:"LND" namespace:"lnd"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var cfg config
|
var cfg config
|
||||||
|
|
||||||
var backend backends.Backend
|
var backend backends.Backend
|
||||||
var backendName string
|
|
||||||
|
|
||||||
func initConfig() {
|
func initConfig() {
|
||||||
lndDir := getDefaultLndDir()
|
lndDir := getDefaultLndDir()
|
||||||
|
@ -44,6 +57,12 @@ func initConfig() {
|
||||||
LogFile: defaultLogFile,
|
LogFile: defaultLogFile,
|
||||||
LogLevel: defaultLogLevel,
|
LogLevel: defaultLogLevel,
|
||||||
|
|
||||||
|
RESTHost: defaultRESTHost,
|
||||||
|
|
||||||
|
TipMessage: defaultTipMessage,
|
||||||
|
TipExpiry: defaultTipExpiry,
|
||||||
|
DefaultTipValue: defaultTipValue,
|
||||||
|
|
||||||
LND: &backends.LND{
|
LND: &backends.LND{
|
||||||
RPCHost: defaultLndRPCHost,
|
RPCHost: defaultLndRPCHost,
|
||||||
CertFile: path.Join(lndDir, defaultLndCertFile),
|
CertFile: path.Join(lndDir, defaultLndCertFile),
|
||||||
|
@ -82,9 +101,8 @@ func initConfig() {
|
||||||
log.Infof("Failed to parse config file: %v", errFile)
|
log.Infof("Failed to parse config file: %v", errFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: add more backend options like for example c-lighting
|
// TODO: add more backend options like for example c-lighting and eclair
|
||||||
backend = cfg.LND
|
backend = cfg.LND
|
||||||
backendName = "LND"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getDefaultLndDir() (dir string) {
|
func getDefaultLndDir() (dir string) {
|
||||||
|
|
131
lightningtip.go
131
lightningtip.go
|
@ -1,6 +1,31 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import "fmt"
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
type getInvoiceRequest struct {
|
||||||
|
Value int64
|
||||||
|
Message string
|
||||||
|
}
|
||||||
|
|
||||||
|
type invoiceResponse struct {
|
||||||
|
Invoice string
|
||||||
|
Error string
|
||||||
|
}
|
||||||
|
|
||||||
|
type tipValueResponse struct {
|
||||||
|
TipValue int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type errorResponse struct {
|
||||||
|
Error string
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
initLog()
|
initLog()
|
||||||
|
@ -10,12 +35,108 @@ func main() {
|
||||||
err := backend.Connect()
|
err := backend.Connect()
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
log.Info("Successfully connected to " + backendName)
|
http.HandleFunc("/", notFoundHandler)
|
||||||
|
http.HandleFunc("/getinvoice", getInvoiceHandler)
|
||||||
|
http.HandleFunc("/defaulttipvalue", defaultTipValueHandler)
|
||||||
|
|
||||||
invoice, err := backend.GetInvoice("Just a test", 1, 3600)
|
log.Info("Subscribing to invoices")
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
err = cfg.LND.SubscribeInvoices()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Failed to subscribe to invoices: " + fmt.Sprint(err))
|
||||||
|
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
}()
|
||||||
|
|
||||||
|
log.Info("Starting HTTP server")
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
err = http.ListenAndServe(cfg.RESTHost, nil)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Failed to start HTTP server: " + fmt.Sprint(err))
|
||||||
|
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {}
|
||||||
|
|
||||||
log.Info("Got invoice " + invoice)
|
|
||||||
log.Info(fmt.Sprint(err))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getInvoiceHandler(writer http.ResponseWriter, request *http.Request) {
|
||||||
|
var errorMessage string
|
||||||
|
|
||||||
|
tipValue := cfg.DefaultTipValue
|
||||||
|
tipMessage := cfg.TipMessage
|
||||||
|
|
||||||
|
if request.Method == http.MethodPost {
|
||||||
|
var body getInvoiceRequest
|
||||||
|
|
||||||
|
data, _ := ioutil.ReadAll(request.Body)
|
||||||
|
|
||||||
|
err := json.Unmarshal(data, &body)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
if body.Value != 0 {
|
||||||
|
tipValue = body.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
if body.Message != "" {
|
||||||
|
tipMessage = body.Message
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
errorMessage = "Could not parse values from request"
|
||||||
|
|
||||||
|
log.Warning(errorMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
invoice, err := backend.GetInvoice(tipMessage, tipValue, cfg.TipExpiry)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
log.Info("Created invoice with value of " + strconv.FormatInt(tipValue, 10) + " satoshis")
|
||||||
|
|
||||||
|
writer.Write(marshalJson(invoiceResponse{
|
||||||
|
Invoice: invoice,
|
||||||
|
Error: errorMessage,
|
||||||
|
}))
|
||||||
|
|
||||||
|
} else {
|
||||||
|
errorMessage := "Failed to create invoice"
|
||||||
|
|
||||||
|
log.Error(errorMessage + ": " + fmt.Sprint(err))
|
||||||
|
|
||||||
|
writer.Write(marshalJson(errorResponse{
|
||||||
|
Error: errorMessage,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultTipValueHandler(writer http.ResponseWriter, request *http.Request) {
|
||||||
|
writer.Write(marshalJson(tipValueResponse{
|
||||||
|
TipValue: cfg.DefaultTipValue,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func notFoundHandler(writer http.ResponseWriter, request *http.Request) {
|
||||||
|
writer.Write(marshalJson(errorResponse{
|
||||||
|
Error: "Not found",
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func marshalJson(data interface{}) []byte {
|
||||||
|
response, _ := json.MarshalIndent(data, "", " ")
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue