This commit is contained in:
bumi 2019-02-20 01:31:03 +01:00
parent 01fb7be447
commit faf858f0c4
2 changed files with 104 additions and 106 deletions

View File

@ -13,8 +13,8 @@ import (
var stdOutLogger = log.New(os.Stdout, "", log.LstdFlags) var stdOutLogger = log.New(os.Stdout, "", log.LstdFlags)
type Invoice struct { type Invoice struct {
Value int64 `json:"value"` Value int64 `json:"value"`
Memo string `json:"memo"` Memo string `json:"memo"`
} }
func main() { func main() {
@ -22,21 +22,21 @@ func main() {
certFile := flag.String("cert", "~/.lnd/tls.cert", "Path to the lnd tls.cert file") certFile := flag.String("cert", "~/.lnd/tls.cert", "Path to the lnd tls.cert file")
macaroonFile := flag.String("macaroon", "~/.lnd/data/chain/bitcoin/mainnet/invoice.macaroon", "Path to the lnd macaroon file") macaroonFile := flag.String("macaroon", "~/.lnd/data/chain/bitcoin/mainnet/invoice.macaroon", "Path to the lnd macaroon file")
bind := flag.String("bind", ":1323", "Host and port to bind on") bind := flag.String("bind", ":1323", "Host and port to bind on")
staticPath := flag.String("static-path", "", "Path to a static assets directory. Blank to disable serving static files") staticPath := flag.String("static-path", "", "Path to a static assets directory. Blank to disable serving static files")
disableCors := flag.Bool("disable-cors", false, "Disable CORS headers") disableCors := flag.Bool("disable-cors", false, "Disable CORS headers")
flag.Parse() flag.Parse()
e := echo.New() e := echo.New()
if (*staticPath != "") { if *staticPath != "" {
e.Static("/static", *staticPath) e.Static("/static", *staticPath)
} }
if (!*disableCors) { if !*disableCors {
e.Use(middleware.CORS()) e.Use(middleware.CORS())
} }
e.Use(middleware.Recover()) e.Use(middleware.Recover())
stdOutLogger.Printf("Connection to %s using macaroon %s and cert %s", *address, *macaroonFile, *certFile) stdOutLogger.Printf("Connection to %s using macaroon %s and cert %s", *address, *macaroonFile, *certFile)
lndOptions := ln.LNDoptions{ lndOptions := ln.LNDoptions{
Address: *address, Address: *address,
CertFile: *certFile, CertFile: *certFile,
@ -44,11 +44,11 @@ func main() {
} }
lnClient, err := ln.NewLNDclient(lndOptions) lnClient, err := ln.NewLNDclient(lndOptions)
if err != nil { if err != nil {
stdOutLogger.Print("Error initializing LND client:") stdOutLogger.Print("Error initializing LND client:")
panic(err) panic(err)
} }
// endpoint URLs compatible to the LND REST API // endpoint URLs compatible to the LND REST API
e.POST("/v1/invoices", func(c echo.Context) error { e.POST("/v1/invoices", func(c echo.Context) error {
i := new(Invoice) i := new(Invoice)
if err := c.Bind(i); err != nil { if err := c.Bind(i); err != nil {

184
ln/lnd.go
View File

@ -1,138 +1,136 @@
package ln package ln
import ( import (
"context" "context"
"encoding/hex" "encoding/hex"
"io/ioutil" "io/ioutil"
"log" "log"
"os" "os"
"github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc"
"gopkg.in/macaroon.v2" "github.com/lightningnetwork/lnd/macaroons"
"github.com/lightningnetwork/lnd/macaroons" "gopkg.in/macaroon.v2"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials"
) )
var stdOutLogger = log.New(os.Stdout, "", log.LstdFlags) var stdOutLogger = log.New(os.Stdout, "", log.LstdFlags)
// thanks https://github.com/philippgille/ln-paywall/ // thanks https://github.com/philippgille/ln-paywall/
// Invoice is a Lightning Network invoice and contains the typical invoice string and the payment hash. // Invoice is a Lightning Network invoice and contains the typical invoice string and the payment hash.
type Invoice struct { type Invoice struct {
PaymentHash string `json:"payment_hash"` PaymentHash string `json:"payment_hash"`
PaymentRequest string `json:"payment_request"` PaymentRequest string `json:"payment_request"`
Settled bool `json:"settled"` Settled bool `json:"settled"`
} }
type LNDclient struct { type LNDclient struct {
lndClient lnrpc.LightningClient lndClient lnrpc.LightningClient
ctx context.Context ctx context.Context
conn *grpc.ClientConn conn *grpc.ClientConn
} }
// AddInvoice generates an invoice with the given price and memo. // AddInvoice generates an invoice with the given price and memo.
func (c LNDclient) AddInvoice(value int64, memo string) (Invoice, error) { func (c LNDclient) AddInvoice(value int64, memo string) (Invoice, error) {
result := Invoice{} result := Invoice{}
stdOutLogger.Printf("Creating invoice: memo=%s amount=%v ", memo, value) stdOutLogger.Printf("Creating invoice: memo=%s amount=%v ", memo, value)
invoice := lnrpc.Invoice{ invoice := lnrpc.Invoice{
Memo: memo, Memo: memo,
Value: value, Value: value,
} }
res, err := c.lndClient.AddInvoice(c.ctx, &invoice) res, err := c.lndClient.AddInvoice(c.ctx, &invoice)
if err != nil { if err != nil {
return result, err return result, err
} }
result.PaymentHash = hex.EncodeToString(res.RHash) result.PaymentHash = hex.EncodeToString(res.RHash)
result.PaymentRequest = res.PaymentRequest result.PaymentRequest = res.PaymentRequest
return result, nil return result, nil
} }
// GetInvoice takes an invoice ID and returns the invoice details including settlement details // GetInvoice takes an invoice ID and returns the invoice details including settlement details
// An error is returned if no corresponding invoice was found. // An error is returned if no corresponding invoice was found.
func (c LNDclient) GetInvoice(paymentHashStr string) (Invoice, error) { func (c LNDclient) GetInvoice(paymentHashStr string) (Invoice, error) {
var invoice Invoice var invoice Invoice
stdOutLogger.Printf("Lookup invoice: hash=%s\n", paymentHashStr) stdOutLogger.Printf("Lookup invoice: hash=%s\n", paymentHashStr)
plainHash, err := hex.DecodeString(paymentHashStr) plainHash, err := hex.DecodeString(paymentHashStr)
if err != nil { if err != nil {
return invoice, err return invoice, err
} }
// Get the invoice for that hash // Get the invoice for that hash
paymentHash := lnrpc.PaymentHash{ paymentHash := lnrpc.PaymentHash{
RHash: plainHash, RHash: plainHash,
// Hex encoded, must be exactly 32 byte // Hex encoded, must be exactly 32 byte
RHashStr: paymentHashStr, RHashStr: paymentHashStr,
} }
result, err := c.lndClient.LookupInvoice(c.ctx, &paymentHash) result, err := c.lndClient.LookupInvoice(c.ctx, &paymentHash)
if err != nil { if err != nil {
return invoice, err return invoice, err
} }
invoice = Invoice{} invoice = Invoice{}
invoice.PaymentHash = hex.EncodeToString(result.RHash) invoice.PaymentHash = hex.EncodeToString(result.RHash)
invoice.PaymentRequest = result.PaymentRequest invoice.PaymentRequest = result.PaymentRequest
invoice.Settled = result.GetSettled() invoice.Settled = result.GetSettled()
return invoice, nil return invoice, nil
} }
func NewLNDclient(lndOptions LNDoptions) (LNDclient, error) { func NewLNDclient(lndOptions LNDoptions) (LNDclient, error) {
result := LNDclient{} result := LNDclient{}
creds, err := credentials.NewClientTLSFromFile(lndOptions.CertFile, "") creds, err := credentials.NewClientTLSFromFile(lndOptions.CertFile, "")
if err != nil { if err != nil {
return result, err return result, err
} }
opts := []grpc.DialOption{ opts := []grpc.DialOption{
grpc.WithTransportCredentials(creds), grpc.WithTransportCredentials(creds),
} }
macaroonData, err := ioutil.ReadFile(lndOptions.MacaroonFile) macaroonData, err := ioutil.ReadFile(lndOptions.MacaroonFile)
if err != nil { if err != nil {
return result, err return result, err
} }
mac := &macaroon.Macaroon{} mac := &macaroon.Macaroon{}
if err = mac.UnmarshalBinary(macaroonData); err != nil { if err = mac.UnmarshalBinary(macaroonData); err != nil {
return result, err return result, err
} }
macCred := macaroons.NewMacaroonCredential(mac) macCred := macaroons.NewMacaroonCredential(mac)
opts = append(opts, grpc.WithPerRPCCredentials(macCred)) opts = append(opts, grpc.WithPerRPCCredentials(macCred))
conn, err := grpc.Dial(lndOptions.Address, opts...) conn, err := grpc.Dial(lndOptions.Address, opts...)
if err != nil { if err != nil {
return result, err return result, err
} }
c := lnrpc.NewLightningClient(conn) c := lnrpc.NewLightningClient(conn)
result = LNDclient{ result = LNDclient{
conn: conn, conn: conn,
ctx: context.Background(), ctx: context.Background(),
lndClient: c, lndClient: c,
} }
return result, nil return result, nil
} }
// LNDoptions are the options for the connection to the lnd node. // LNDoptions are the options for the connection to the lnd node.
type LNDoptions struct { type LNDoptions struct {
// Address of your LND node, including the port. // Address of your LND node, including the port.
// Optional ("localhost:10009" by default). // Optional ("localhost:10009" by default).
Address string Address string
// Path to the "tls.cert" file that your LND node uses. // Path to the "tls.cert" file that your LND node uses.
// Optional ("tls.cert" by default). // Optional ("tls.cert" by default).
CertFile string CertFile string
// Path to the macaroon file that your LND node uses. // Path to the macaroon file that your LND node uses.
// "invoice.macaroon" if you only use the AddInvoice() and GetInvoice() methods // "invoice.macaroon" if you only use the AddInvoice() and GetInvoice() methods
// (required by the middleware in the package "wall"). // (required by the middleware in the package "wall").
// "admin.macaroon" if you use the Pay() method (required by the client in the package "pay"). // "admin.macaroon" if you use the Pay() method (required by the client in the package "pay").
// Optional ("invoice.macaroon" by default). // Optional ("invoice.macaroon" by default).
MacaroonFile string MacaroonFile string
} }