Skip to content

Commit

Permalink
mint - fake lightning backend
Browse files Browse the repository at this point in the history
  • Loading branch information
elnosh committed Oct 26, 2024
1 parent db58e26 commit 9f305a4
Show file tree
Hide file tree
Showing 3 changed files with 184 additions and 39 deletions.
4 changes: 3 additions & 1 deletion .env.mint.example
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ MINTING_MAX_AMOUNT=50000
# max melt amount (in sats)
MELTING_MAX_AMOUNT=50000

# Lightning Backend
# Lightning Backend - Lnd, FakeBackend (FOR TESTING ONLY)
LIGHTNING_BACKEND="Lnd"

# LND
LND_GRPC_HOST="127.0.0.1:10001"
LND_CERT_PATH="/path/to/tls/cert"
LND_MACAROON_PATH="/path/to/macaroon"
85 changes: 47 additions & 38 deletions cmd/mint/mint.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,47 +89,56 @@ func configFromEnv() (*mint.Config, error) {
}
mintInfo.Contact = mintContactInfo

// read values for setting up LND
host := os.Getenv("LND_GRPC_HOST")
if host == "" {
return nil, errors.New("LND_GRPC_HOST cannot be empty")
}
certPath := os.Getenv("LND_CERT_PATH")
if certPath == "" {
return nil, errors.New("LND_CERT_PATH cannot be empty")
}
macaroonPath := os.Getenv("LND_MACAROON_PATH")
if macaroonPath == "" {
return nil, errors.New("LND_MACAROON_PATH cannot be empty")
}
var lightningClient lightning.Client

switch os.Getenv("LIGHTNING_BACKEND") {
case "Lnd":
// read values for setting up LND
host := os.Getenv("LND_GRPC_HOST")
if host == "" {
return nil, errors.New("LND_GRPC_HOST cannot be empty")
}
certPath := os.Getenv("LND_CERT_PATH")
if certPath == "" {
return nil, errors.New("LND_CERT_PATH cannot be empty")
}
macaroonPath := os.Getenv("LND_MACAROON_PATH")
if macaroonPath == "" {
return nil, errors.New("LND_MACAROON_PATH cannot be empty")
}

creds, err := credentials.NewClientTLSFromFile(certPath, "")
if err != nil {
return nil, err
}
creds, err := credentials.NewClientTLSFromFile(certPath, "")
if err != nil {
return nil, err
}

macaroonBytes, err := os.ReadFile(macaroonPath)
if err != nil {
return nil, fmt.Errorf("error reading macaroon: os.ReadFile %v", err)
}
macaroonBytes, err := os.ReadFile(macaroonPath)
if err != nil {
return nil, fmt.Errorf("error reading macaroon: os.ReadFile %v", err)
}

macaroon := &macaroon.Macaroon{}
if err = macaroon.UnmarshalBinary(macaroonBytes); err != nil {
return nil, fmt.Errorf("unable to decode macaroon: %v", err)
}
macarooncreds, err := macaroons.NewMacaroonCredential(macaroon)
if err != nil {
return nil, fmt.Errorf("error setting macaroon creds: %v", err)
}
lndConfig := lightning.LndConfig{
GRPCHost: host,
Cert: creds,
Macaroon: macarooncreds,
}
macaroon := &macaroon.Macaroon{}
if err = macaroon.UnmarshalBinary(macaroonBytes); err != nil {
return nil, fmt.Errorf("unable to decode macaroon: %v", err)
}
macarooncreds, err := macaroons.NewMacaroonCredential(macaroon)
if err != nil {
return nil, fmt.Errorf("error setting macaroon creds: %v", err)
}
lndConfig := lightning.LndConfig{
GRPCHost: host,
Cert: creds,
Macaroon: macarooncreds,
}

lndClient, err := lightning.SetupLndClient(lndConfig)
if err != nil {
return nil, fmt.Errorf("error setting LND client: %v", err)
lightningClient, err = lightning.SetupLndClient(lndConfig)
if err != nil {
return nil, fmt.Errorf("error setting LND client: %v", err)
}
case "FakeBackend":
lightningClient = &lightning.FakeBackend{}
default:
return nil, errors.New("invalid lightning backend")
}

logLevel := mint.Info
Expand All @@ -145,7 +154,7 @@ func configFromEnv() (*mint.Config, error) {
InputFeePpk: inputFeePpk,
MintInfo: mintInfo,
Limits: mintLimits,
LightningClient: lndClient,
LightningClient: lightningClient,
LogLevel: logLevel,
}, nil
}
Expand Down
134 changes: 134 additions & 0 deletions mint/lightning/fakebackend.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package lightning

import (
"context"
"crypto/rand"
"crypto/sha256"
"encoding/hex"
"errors"
"fmt"
"slices"
"time"

"github.com/btcsuite/btcd/chaincfg"
"github.com/decred/dcrd/dcrec/secp256k1/v4"
"github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/zpay32"
decodepay "github.com/nbd-wtf/ln-decodepay"
)

const (
FakePreimage = "0000000000000000"
)

type FakeBackend struct {
invoices []Invoice
}

func (fb *FakeBackend) ConnectionStatus() error { return nil }

func (fb *FakeBackend) CreateInvoice(amount uint64) (Invoice, error) {
req, preimage, paymentHash, err := createFakeInvoice(amount)
if err != nil {
return Invoice{}, err
}

invoice := Invoice{
PaymentRequest: req,
PaymentHash: paymentHash,
Preimage: preimage,
Settled: true,
Amount: amount,
Expiry: uint64(time.Now().Unix()),
}
fb.invoices = append(fb.invoices, invoice)

return invoice, nil
}

func (fb *FakeBackend) InvoiceStatus(hash string) (Invoice, error) {
invoiceIdx := slices.IndexFunc(fb.invoices, func(i Invoice) bool {
return i.PaymentHash == hash
})
if invoiceIdx == -1 {
return Invoice{}, errors.New("invoice does not exist")
}

return fb.invoices[invoiceIdx], nil
}

func (fb *FakeBackend) SendPayment(ctx context.Context, request string, amount uint64) (PaymentStatus, error) {
invoice, err := decodepay.Decodepay(request)
if err != nil {
return PaymentStatus{}, fmt.Errorf("error decoding invoice: %v", err)
}

outgoingPayment := Invoice{
PaymentRequest: request,
PaymentHash: invoice.PaymentHash,
Preimage: FakePreimage,
Settled: true,
}
fb.invoices = append(fb.invoices, outgoingPayment)

return PaymentStatus{
Preimage: FakePreimage,
PaymentStatus: Succeeded,
}, nil
}

func (fb *FakeBackend) OutgoingPaymentStatus(ctx context.Context, hash string) (PaymentStatus, error) {
invoiceIdx := slices.IndexFunc(fb.invoices, func(i Invoice) bool {
return i.PaymentHash == hash
})
if invoiceIdx == -1 {
return PaymentStatus{}, errors.New("payment does not exist")
}

return PaymentStatus{
Preimage: fb.invoices[invoiceIdx].Preimage,
PaymentStatus: Succeeded,
}, nil
}

func (fb *FakeBackend) FeeReserve(amount uint64) uint64 {
return 0
}

func createFakeInvoice(amount uint64) (string, string, string, error) {
var random [32]byte
_, err := rand.Read(random[:])
if err != nil {
return "", "", "", err
}
preimage := hex.EncodeToString(random[:])
paymentHash := sha256.Sum256(random[:])
hash := hex.EncodeToString(paymentHash[:])

invoice, err := zpay32.NewInvoice(
&chaincfg.SigNetParams,
paymentHash,
time.Now(),
zpay32.Amount(lnwire.MilliSatoshi(amount*1000)),
zpay32.Description("test"),
)
if err != nil {
return "", "", "", err
}

invoiceStr, err := invoice.Encode(zpay32.MessageSigner{
SignCompact: func(msg []byte) ([]byte, error) {
key, err := secp256k1.GeneratePrivateKey()
if err != nil {
return []byte{}, err
}
return ecdsa.SignCompact(key, msg, true), nil
},
})
if err != nil {
return "", "", "", err
}

return invoiceStr, preimage, hash, nil
}

0 comments on commit 9f305a4

Please sign in to comment.