4
4
"context"
5
5
"fmt"
6
6
"io"
7
- "math"
8
7
"strings"
9
8
"sync"
10
9
"time"
@@ -13,6 +12,12 @@ import (
13
12
"github.com/lightningnetwork/lnd/lntypes"
14
13
)
15
14
15
+ const (
16
+ // invoiceQueryPageSize is the maximum number of invoices that will be
17
+ // queried in a single request.
18
+ invoiceQueryPageSize = 1000
19
+ )
20
+
16
21
// LndChallenger is a challenger that uses an lnd backend to create new L402
17
22
// payment challenges.
18
23
type LndChallenger struct {
@@ -74,7 +79,7 @@ func NewLndChallenger(client InvoiceClient,
74
79
75
80
// Start starts the challenger's main work which is to keep track of all
76
81
// invoices and their states. For that the backing lnd node is queried for all
77
- // invoices on startup and the a subscription to all subsequent invoice updates
82
+ // invoices on startup and a subscription to all subsequent invoice updates
78
83
// is created.
79
84
func (l * LndChallenger ) Start () error {
80
85
// These are the default values for the subscription. In case there are
@@ -84,49 +89,64 @@ func (l *LndChallenger) Start() error {
84
89
addIndex := uint64 (0 )
85
90
settleIndex := uint64 (0 )
86
91
87
- // Get a list of all existing invoices on startup and add them to our
88
- // cache. We need to keep track of all invoices, even quite old ones to
89
- // make sure tokens are valid. But to save space we only keep track of
90
- // an invoice's state .
92
+ log . Debugf ( "Starting LND challenger" )
93
+ // Paginate through all existing invoices on startup and add them to our
94
+ // cache. We need to keep track of all invoices to ensure tokens are
95
+ // valid .
91
96
ctx := l .clientCtx ()
92
- invoiceResp , err := l .client .ListInvoices (
93
- ctx , & lnrpc.ListInvoiceRequest {
94
- NumMaxInvoices : math .MaxUint64 ,
95
- },
96
- )
97
- if err != nil {
98
- return err
99
- }
100
-
101
- // Advance our indices to the latest known one so we'll only receive
102
- // updates for new invoices and/or newly settled invoices.
103
- l .invoicesMtx .Lock ()
104
- for _ , invoice := range invoiceResp .Invoices {
105
- // Some invoices like AMP invoices may not have a payment hash
106
- // populated.
107
- if invoice .RHash == nil {
108
- continue
97
+ indexOffset := uint64 (0 )
98
+ for {
99
+ log .Debugf ("Querying invoices from index %d" , indexOffset )
100
+ invoiceResp , err := l .client .ListInvoices (
101
+ ctx , & lnrpc.ListInvoiceRequest {
102
+ IndexOffset : indexOffset ,
103
+ NumMaxInvoices : invoiceQueryPageSize ,
104
+ },
105
+ )
106
+ if err != nil {
107
+ return err
109
108
}
110
109
111
- if invoice .AddIndex > addIndex {
112
- addIndex = invoice .AddIndex
113
- }
114
- if invoice .SettleIndex > settleIndex {
115
- settleIndex = invoice .SettleIndex
116
- }
117
- hash , err := lntypes .MakeHash (invoice .RHash )
118
- if err != nil {
119
- l .invoicesMtx .Unlock ()
120
- return fmt .Errorf ("error parsing invoice hash: %v" , err )
110
+ // If there are no more invoices, stop pagination.
111
+ if len (invoiceResp .Invoices ) == 0 {
112
+ break
121
113
}
122
114
123
- // Don't track the state of canceled or expired invoices.
124
- if invoiceIrrelevant (invoice ) {
125
- continue
115
+ // Lock the mutex to safely update the invoice states.
116
+ l .invoicesMtx .Lock ()
117
+ for _ , invoice := range invoiceResp .Invoices {
118
+ // Skip invoices that do not have a payment hash
119
+ // populated.
120
+ if invoice .RHash == nil {
121
+ continue
122
+ }
123
+
124
+ if invoice .AddIndex > addIndex {
125
+ addIndex = invoice .AddIndex
126
+ }
127
+ if invoice .SettleIndex > settleIndex {
128
+ settleIndex = invoice .SettleIndex
129
+ }
130
+ hash , err := lntypes .MakeHash (invoice .RHash )
131
+ if err != nil {
132
+ l .invoicesMtx .Unlock ()
133
+ return fmt .Errorf ("error parsing invoice " +
134
+ "hash: %v" , err )
135
+ }
136
+
137
+ // Skip tracking the state of canceled or expired
138
+ // invoices.
139
+ if invoiceIrrelevant (invoice ) {
140
+ continue
141
+ }
142
+ l .invoiceStates [hash ] = invoice .State
126
143
}
127
- l .invoiceStates [hash ] = invoice .State
144
+ l .invoicesMtx .Unlock ()
145
+
146
+ // Update the index offset for the next batch.
147
+ indexOffset = invoiceResp .LastIndexOffset
128
148
}
129
- l . invoicesMtx . Unlock ( )
149
+ log . Debugf ( "Finished querying invoices" )
130
150
131
151
// We need to be able to cancel any subscription we make.
132
152
ctxc , cancel := context .WithCancel (l .clientCtx ())
0 commit comments