Skip to content

Commit

Permalink
mint - refactor melt flow, add call to check status of outgoing payment
Browse files Browse the repository at this point in the history
  • Loading branch information
elnosh committed Sep 30, 2024
1 parent c5727cc commit 51bd766
Show file tree
Hide file tree
Showing 11 changed files with 382 additions and 67 deletions.
1 change: 1 addition & 0 deletions cashu/cashu.go
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,7 @@ var (
NoProofsProvided = Error{Detail: "no proofs provided", Code: InvalidProofErrCode}
DuplicateProofs = Error{Detail: "duplicate proofs", Code: InvalidProofErrCode}
QuoteNotExistErr = Error{Detail: "quote does not exist", Code: QuoteErrCode}
MeltQuotePending = Error{Detail: "quote is pending", Code: MeltQuotePendingErrCode}
MeltQuoteAlreadyPaid = Error{Detail: "quote already paid", Code: MeltQuoteAlreadyPaidErrCode}
MeltAmountExceededErr = Error{Detail: "max amount for melting exceeded", Code: AmountLimitExceeded}
InsufficientProofsAmount = Error{
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ require (
github.com/btcsuite/btcd/btcec/v2 v2.3.3
github.com/btcsuite/btcd/btcutil v1.1.5
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0
github.com/elnosh/btc-docker-test v0.0.0-20240730150514-6d94d76b8881
github.com/elnosh/btc-docker-test v0.0.0-20240927160251-93a4da3d1754
github.com/fxamacker/cbor/v2 v2.7.0
github.com/golang-migrate/migrate/v4 v4.17.1
github.com/gorilla/mux v1.8.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,8 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/elnosh/btc-docker-test v0.0.0-20240730150514-6d94d76b8881 h1:iHr0CRNKU9ilxf+LGUon9XB39lRvLlbbm9C9dx2Y/u0=
github.com/elnosh/btc-docker-test v0.0.0-20240730150514-6d94d76b8881/go.mod h1:W2G5BhKocXfbC61N4Jy8Z+0rSPGAbDcZsKIr+4B5v9Y=
github.com/elnosh/btc-docker-test v0.0.0-20240927160251-93a4da3d1754 h1:LYVrpWL+RI13UBb36U0TkFu09X7+haLR/ix2zJVQ+Ac=
github.com/elnosh/btc-docker-test v0.0.0-20240927160251-93a4da3d1754/go.mod h1:OZ/LMGKylMDHiAh47vr2MjzJGzE2iRafZyqseh7RppA=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
Expand Down
18 changes: 17 additions & 1 deletion mint/lightning/lightning.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package lightning

import "context"

// Client interface to interact with a Lightning backend
type Client interface {
CreateInvoice(amount uint64) (Invoice, error)
InvoiceStatus(hash string) (Invoice, error)
SendPayment(request string, amount uint64) (string, error)
SendPayment(ctx context.Context, request string, amount uint64) (PaymentStatus, error)
OutgoingPaymentStatus(ctx context.Context, hash string) (PaymentStatus, error)
FeeReserve(amount uint64) uint64
}

Expand All @@ -16,3 +19,16 @@ type Invoice struct {
Amount uint64
Expiry uint64
}

type State int

const (
Succeeded State = iota
Failed
Pending
)

type PaymentStatus struct {
Preimage string
PaymentStatus State
}
68 changes: 53 additions & 15 deletions mint/lightning/lnd.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"time"

"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
"github.com/lightningnetwork/lnd/macaroons"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
Expand All @@ -26,7 +27,8 @@ type LndConfig struct {
}

type LndClient struct {
grpcClient lnrpc.LightningClient
grpcClient lnrpc.LightningClient
routerClient routerrpc.RouterClient
}

func SetupLndClient(config LndConfig) (*LndClient, error) {
Expand All @@ -41,7 +43,8 @@ func SetupLndClient(config LndConfig) (*LndClient, error) {
}

grpcClient := lnrpc.NewLightningClient(conn)
return &LndClient{grpcClient: grpcClient}, nil
routerClient := routerrpc.NewRouterClient(conn)
return &LndClient{grpcClient: grpcClient, routerClient: routerClient}, nil
}

func (lnd *LndClient) CreateInvoice(amount uint64) (Invoice, error) {
Expand Down Expand Up @@ -89,31 +92,66 @@ func (lnd *LndClient) InvoiceStatus(hash string) (Invoice, error) {
return invoice, nil
}

type SendPaymentResponse struct {
PaymentError string `json:"payment_error"`
PaymentPreimage string `json:"payment_preimage"`
}

func (lnd *LndClient) SendPayment(request string, amount uint64) (string, error) {
func (lnd *LndClient) SendPayment(ctx context.Context, request string, amount uint64) (PaymentStatus, error) {
feeLimit := lnd.FeeReserve(amount)
sendPaymentRequest := lnrpc.SendRequest{
PaymentRequest: request,
FeeLimit: &lnrpc.FeeLimit{Limit: &lnrpc.FeeLimit_Fixed{Fixed: int64(feeLimit)}},
}

sendPaymentResponse, err := lnd.grpcClient.SendPaymentSync(context.Background(), &sendPaymentRequest)
sendPaymentResponse, err := lnd.grpcClient.SendPaymentSync(ctx, &sendPaymentRequest)
if err != nil {
return "", fmt.Errorf("error making payment: %v", err)
// if context deadline is exceeded (1 min), mark payment as pending
// if any other error, mark as failed
if errors.Is(ctx.Err(), context.DeadlineExceeded) {
return PaymentStatus{PaymentStatus: Pending}, nil
} else {
return PaymentStatus{PaymentStatus: Failed}, err
}
}
if len(sendPaymentResponse.PaymentError) > 0 {
return "", fmt.Errorf("error making payment: %v", sendPaymentResponse.PaymentError)
}
if len(sendPaymentResponse.PaymentPreimage) == 0 {
return "", fmt.Errorf("could not make payment")
return PaymentStatus{PaymentStatus: Failed}, fmt.Errorf("payment error: %v", sendPaymentResponse.PaymentError)
}

preimage := hex.EncodeToString(sendPaymentResponse.PaymentPreimage)
return preimage, nil
paymentResponse := PaymentStatus{Preimage: preimage, PaymentStatus: Succeeded}
return paymentResponse, nil
}

func (lnd *LndClient) OutgoingPaymentStatus(ctx context.Context, hash string) (PaymentStatus, error) {
hashBytes, err := hex.DecodeString(hash)
if err != nil {
return PaymentStatus{}, errors.New("invalid hash provided")
}

trackPaymentRequest := routerrpc.TrackPaymentRequest{
PaymentHash: hashBytes,
// setting this to only get the final payment update
NoInflightUpdates: true,
}

trackPaymentStream, err := lnd.routerClient.TrackPaymentV2(ctx, &trackPaymentRequest)
if err != nil {
return PaymentStatus{PaymentStatus: Failed}, err
}

// this should block until final payment update
payment, err := trackPaymentStream.Recv()
if err != nil {
return PaymentStatus{PaymentStatus: Failed}, fmt.Errorf("payment failed: %w", err)
}
if payment.Status == lnrpc.Payment_UNKNOWN || payment.Status == lnrpc.Payment_FAILED {
return PaymentStatus{PaymentStatus: Failed},
fmt.Errorf("payment failed: %s", payment.FailureReason.String())
}
if payment.Status == lnrpc.Payment_IN_FLIGHT {
return PaymentStatus{PaymentStatus: Pending}, nil
}
if payment.Status == lnrpc.Payment_SUCCEEDED {
return PaymentStatus{PaymentStatus: Succeeded, Preimage: payment.PaymentPreimage}, nil
}

return PaymentStatus{PaymentStatus: Failed}, errors.New("unknown")
}

func (lnd *LndClient) FeeReserve(amount uint64) uint64 {
Expand Down
Loading

0 comments on commit 51bd766

Please sign in to comment.