Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor melt flow #63

Merged
merged 2 commits into from
Oct 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion cashu/cashu.go
Original file line number Diff line number Diff line change
Expand Up @@ -422,11 +422,13 @@ var (
MintingDisabled = Error{Detail: "minting is disabled", Code: MintingDisabledErrCode}
MintAmountExceededErr = Error{Detail: "max amount for minting exceeded", Code: AmountLimitExceeded}
OutputsOverQuoteAmountErr = Error{Detail: "sum of the output amounts is greater than quote amount", Code: StandardErrCode}
ProofAlreadyUsedErr = Error{Detail: "proofs already used", Code: ProofAlreadyUsedErrCode}
ProofAlreadyUsedErr = Error{Detail: "proof already used", Code: ProofAlreadyUsedErrCode}
ProofPendingErr = Error{Detail: "proof is pending", Code: ProofAlreadyUsedErrCode}
InvalidProofErr = Error{Detail: "invalid proof", Code: InvalidProofErrCode}
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
Loading