Skip to content

Commit

Permalink
challenger: paginage ListInvoices to avoid resource exhaustion
Browse files Browse the repository at this point in the history
  • Loading branch information
bhandras committed Jan 13, 2025
1 parent 25ef16b commit 6f11a96
Showing 1 changed file with 58 additions and 38 deletions.
96 changes: 58 additions & 38 deletions challenger/lnd.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"
"fmt"
"io"
"math"
"strings"
"sync"
"time"
Expand All @@ -13,6 +12,12 @@ import (
"github.com/lightningnetwork/lnd/lntypes"
)

const (
// invoiceQueryPageSize is the maximum number of invoices that will be
// queried in a single request.
invoiceQueryPageSize = 1000
)

// LndChallenger is a challenger that uses an lnd backend to create new L402
// payment challenges.
type LndChallenger struct {
Expand Down Expand Up @@ -74,7 +79,7 @@ func NewLndChallenger(client InvoiceClient,

// Start starts the challenger's main work which is to keep track of all
// invoices and their states. For that the backing lnd node is queried for all
// invoices on startup and the a subscription to all subsequent invoice updates
// invoices on startup and a subscription to all subsequent invoice updates
// is created.
func (l *LndChallenger) Start() error {
// These are the default values for the subscription. In case there are
Expand All @@ -84,49 +89,64 @@ func (l *LndChallenger) Start() error {
addIndex := uint64(0)
settleIndex := uint64(0)

// Get a list of all existing invoices on startup and add them to our
// cache. We need to keep track of all invoices, even quite old ones to
// make sure tokens are valid. But to save space we only keep track of
// an invoice's state.
log.Debugf("Starting LND challenger")
// Paginate through all existing invoices on startup and add them to our
// cache. We need to keep track of all invoices to ensure tokens are
// valid.
ctx := l.clientCtx()
invoiceResp, err := l.client.ListInvoices(
ctx, &lnrpc.ListInvoiceRequest{
NumMaxInvoices: math.MaxUint64,
},
)
if err != nil {
return err
}

// Advance our indices to the latest known one so we'll only receive
// updates for new invoices and/or newly settled invoices.
l.invoicesMtx.Lock()
for _, invoice := range invoiceResp.Invoices {
// Some invoices like AMP invoices may not have a payment hash
// populated.
if invoice.RHash == nil {
continue
indexOffset := uint64(0)
for {
log.Debugf("Querying invoices from index %d", indexOffset)
invoiceResp, err := l.client.ListInvoices(
ctx, &lnrpc.ListInvoiceRequest{
IndexOffset: indexOffset,
NumMaxInvoices: invoiceQueryPageSize,
},
)
if err != nil {
return err
}

if invoice.AddIndex > addIndex {
addIndex = invoice.AddIndex
}
if invoice.SettleIndex > settleIndex {
settleIndex = invoice.SettleIndex
}
hash, err := lntypes.MakeHash(invoice.RHash)
if err != nil {
l.invoicesMtx.Unlock()
return fmt.Errorf("error parsing invoice hash: %v", err)
// If there are no more invoices, stop pagination.
if len(invoiceResp.Invoices) == 0 {
break
}

// Don't track the state of canceled or expired invoices.
if invoiceIrrelevant(invoice) {
continue
// Lock the mutex to safely update the invoice states.
l.invoicesMtx.Lock()
for _, invoice := range invoiceResp.Invoices {
// Skip invoices that do not have a payment hash
// populated.
if invoice.RHash == nil {
continue
}

if invoice.AddIndex > addIndex {
addIndex = invoice.AddIndex
}
if invoice.SettleIndex > settleIndex {
settleIndex = invoice.SettleIndex
}
hash, err := lntypes.MakeHash(invoice.RHash)
if err != nil {
l.invoicesMtx.Unlock()
return fmt.Errorf("error parsing invoice "+
"hash: %v", err)
}

// Skip tracking the state of canceled or expired
// invoices.
if invoiceIrrelevant(invoice) {
continue
}
l.invoiceStates[hash] = invoice.State
}
l.invoiceStates[hash] = invoice.State
l.invoicesMtx.Unlock()

// Update the index offset for the next batch.
indexOffset = invoiceResp.LastIndexOffset
}
l.invoicesMtx.Unlock()
log.Debugf("Finished querying invoices")

// We need to be able to cancel any subscription we make.
ctxc, cancel := context.WithCancel(l.clientCtx())
Expand Down

0 comments on commit 6f11a96

Please sign in to comment.