From 87a0a0c58892add83dfe6372d3149e5e0e34ec2e Mon Sep 17 00:00:00 2001 From: carla Date: Wed, 20 May 2020 13:42:20 +0200 Subject: [PATCH 1/3] lndclient: add lookup invoice function to client implementation --- lndclient/lightning_client.go | 98 +++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/lndclient/lightning_client.go b/lndclient/lightning_client.go index 32dfffe..f09a43f 100644 --- a/lndclient/lightning_client.go +++ b/lndclient/lightning_client.go @@ -16,6 +16,7 @@ import ( "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc" "github.com/lightningnetwork/lnd/lntypes" + "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/zpay32" "google.golang.org/grpc" "google.golang.org/grpc/codes" @@ -365,6 +366,103 @@ func (s *lightningClient) AddInvoice(ctx context.Context, return hash, resp.PaymentRequest, nil } +// Invoice represents an invoice in lnd. +type Invoice struct { + // Preimage is the invoice's preimage, which is set if the invoice + // is settled. + Preimage *lntypes.Preimage + + // Hash is the invoice hash. + Hash lntypes.Hash + + // Memo is an optional memo field for hte invoice. + Memo string + + // PaymentRequest is the invoice's payment request. + PaymentRequest string + + // Amount is the amount of the invoice in millisatoshis. + Amount lnwire.MilliSatoshi + + // AmountPaid is the amount that was paid for the invoice. This field + // will only be set if the invoice is settled. + AmountPaid lnwire.MilliSatoshi + + // CreationDate is the time the invoice was created. + CreationDate time.Time + + // SettleDate is the time the invoice was settled. + SettleDate time.Time + + // State is the invoice's current state. + State channeldb.ContractState + + // IsKeysend indicates whether the invoice was a spontaneous payment. + IsKeysend bool +} + +// LookupInvoice looks up an invoice in lnd, it will error if the invoice is +// not known to lnd. +func (s *lightningClient) LookupInvoice(ctx context.Context, + hash lntypes.Hash) (*Invoice, error) { + + rpcCtx, cancel := context.WithTimeout(ctx, rpcTimeout) + defer cancel() + + rpcIn := &lnrpc.PaymentHash{ + RHash: hash[:], + } + + rpcCtx = s.adminMac.WithMacaroonAuth(rpcCtx) + resp, err := s.client.LookupInvoice(rpcCtx, rpcIn) + if err != nil { + return nil, err + } + + invoice := &Invoice{ + Preimage: nil, + Hash: hash, + Memo: resp.Memo, + PaymentRequest: resp.PaymentRequest, + Amount: lnwire.MilliSatoshi(resp.ValueMsat), + AmountPaid: lnwire.MilliSatoshi(resp.AmtPaidMsat), + CreationDate: time.Unix(resp.CreationDate, 0), + IsKeysend: resp.IsKeysend, + } + + switch resp.State { + case lnrpc.Invoice_OPEN: + invoice.State = channeldb.ContractOpen + + case lnrpc.Invoice_ACCEPTED: + invoice.State = channeldb.ContractAccepted + + // If the invoice is settled, it also has a non-nil preimage, which we + // can set on our invoice. + case lnrpc.Invoice_SETTLED: + invoice.State = channeldb.ContractSettled + preimage, err := lntypes.MakePreimage(resp.RPreimage) + if err != nil { + return nil, err + } + invoice.Preimage = &preimage + + case lnrpc.Invoice_CANCELED: + invoice.State = channeldb.ContractCanceled + + default: + return nil, fmt.Errorf("unknown invoice state: %v", resp.State) + } + + // Only set settle date if it is non-zero, because 0 unix time is + // not the same as a zero time struct. + if resp.SettleDate != 0 { + invoice.SettleDate = time.Unix(resp.SettleDate, 0) + } + + return invoice, nil +} + // ListTransactions returns all known transactions of the backing lnd node. func (s *lightningClient) ListTransactions(ctx context.Context) ([]*wire.MsgTx, error) { rpcCtx, cancel := context.WithTimeout(ctx, rpcTimeout) From 8b1cdd414cbe2d98689aa747a70422c2b9bf1d6a Mon Sep 17 00:00:00 2001 From: carla Date: Wed, 20 May 2020 13:42:20 +0200 Subject: [PATCH 2/3] test/test: add lookup invoice to mock lightning client Track the invoices we create with AddInvoice so that we can realistically lookup and settle with the mock. --- test/lightning_client_mock.go | 30 ++++++++++++++++++++++++++++++ test/lnd_services_mock.go | 5 +++++ 2 files changed, 35 insertions(+) diff --git a/test/lightning_client_mock.go b/test/lightning_client_mock.go index 2801bf9..84d6725 100644 --- a/test/lightning_client_mock.go +++ b/test/lightning_client_mock.go @@ -11,6 +11,7 @@ import ( "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" "github.com/lightninglabs/loop/lndclient" + "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc" "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/zpay32" @@ -125,9 +126,38 @@ func (h *mockLightningClient) AddInvoice(ctx context.Context, return lntypes.Hash{}, "", err } + // Add the invoice we have created to our mock's set of invoices. + h.lnd.Invoices[hash] = &lndclient.Invoice{ + Preimage: nil, + Hash: hash, + PaymentRequest: payReqString, + Amount: in.Value, + CreationDate: creationDate, + State: channeldb.ContractOpen, + IsKeysend: false, + } + return hash, payReqString, nil } +// LookupInvoice looks up an invoice in the mock's set of stored invoices. +// If it is not found, this call will fail. Note that these invoices should +// be settled using settleInvoice to have a preimage, settled state and settled +// date set. +func (h *mockLightningClient) LookupInvoice(_ context.Context, + hash lntypes.Hash) (*lndclient.Invoice, error) { + + h.lnd.lock.Lock() + defer h.lnd.lock.Unlock() + + inv, ok := h.lnd.Invoices[hash] + if !ok { + return nil, fmt.Errorf("invoice: %x not found", hash) + } + + return inv, nil +} + // ListTransactions returns all known transactions of the backing lnd node. func (h *mockLightningClient) ListTransactions( ctx context.Context) ([]*wire.MsgTx, error) { diff --git a/test/lnd_services_mock.go b/test/lnd_services_mock.go index 906b5fd..b340291 100644 --- a/test/lnd_services_mock.go +++ b/test/lnd_services_mock.go @@ -69,6 +69,7 @@ func NewMockLnd() *LndMockServices { NodePubkey: testNodePubkey, Signature: testSignature, SignatureMsg: testSignatureMsg, + Invoices: make(map[lntypes.Hash]*lndclient.Invoice), } lightningClient.lnd = &lnd @@ -158,6 +159,10 @@ type LndMockServices struct { Transactions []*wire.MsgTx + // Invoices is a set of invoices that have been created by the mock, + // keyed by hash string. + Invoices map[lntypes.Hash]*lndclient.Invoice + WaitForFinished func() lock sync.Mutex From a57e34c71f82b5670cd053d84c299d6ea5f9a95d Mon Sep 17 00:00:00 2001 From: carla Date: Wed, 20 May 2020 13:42:21 +0200 Subject: [PATCH 3/3] lndclient: add lookup invoice function to client interface --- lndclient/lightning_client.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lndclient/lightning_client.go b/lndclient/lightning_client.go index f09a43f..57827c2 100644 --- a/lndclient/lightning_client.go +++ b/lndclient/lightning_client.go @@ -39,6 +39,9 @@ type LightningClient interface { AddInvoice(ctx context.Context, in *invoicesrpc.AddInvoiceData) ( lntypes.Hash, string, error) + // LookupInvoice looks up an invoice by hash. + LookupInvoice(ctx context.Context, hash lntypes.Hash) (*Invoice, error) + // ListTransactions returns all known transactions of the backing lnd // node. ListTransactions(ctx context.Context) ([]*wire.MsgTx, error)