• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lightningnetwork / lnd / 15561477203

10 Jun 2025 01:54PM UTC coverage: 58.351% (-10.1%) from 68.487%
15561477203

Pull #9356

github

web-flow
Merge 6440b25db into c6d6d4c0b
Pull Request #9356: lnrpc: add incoming/outgoing channel ids filter to forwarding history request

33 of 36 new or added lines in 2 files covered. (91.67%)

28366 existing lines in 455 files now uncovered.

97715 of 167461 relevant lines covered (58.35%)

1.81 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

64.41
/channeldb/paginate.go
1
package channeldb
2

3
import "github.com/lightningnetwork/lnd/kvdb"
4

5
type paginator struct {
6
        // cursor is the cursor which we are using to iterate through a bucket.
7
        cursor kvdb.RCursor
8

9
        // reversed indicates whether we are paginating forwards or backwards.
10
        reversed bool
11

12
        // indexOffset is the index from which we will begin querying.
13
        indexOffset uint64
14

15
        // totalItems is the total number of items we allow in our response.
16
        totalItems uint64
17
}
18

19
// newPaginator returns a struct which can be used to query an indexed bucket
20
// in pages.
21
func newPaginator(c kvdb.RCursor, reversed bool,
22
        indexOffset, totalItems uint64) paginator {
3✔
23

3✔
24
        return paginator{
3✔
25
                cursor:      c,
3✔
26
                reversed:    reversed,
3✔
27
                indexOffset: indexOffset,
3✔
28
                totalItems:  totalItems,
3✔
29
        }
3✔
30
}
3✔
31

32
// keyValueForIndex seeks our cursor to a given index and returns the key and
33
// value at that position.
34
func (p paginator) keyValueForIndex(index uint64) ([]byte, []byte) {
3✔
35
        var keyIndex [8]byte
3✔
36
        byteOrder.PutUint64(keyIndex[:], index)
3✔
37
        return p.cursor.Seek(keyIndex[:])
3✔
38
}
3✔
39

40
// lastIndex returns the last value in our index, if our index is empty it
41
// returns 0.
UNCOV
42
func (p paginator) lastIndex() uint64 {
×
UNCOV
43
        keyIndex, _ := p.cursor.Last()
×
UNCOV
44
        if keyIndex == nil {
×
45
                return 0
×
46
        }
×
47

UNCOV
48
        return byteOrder.Uint64(keyIndex)
×
49
}
50

51
// nextKey is a helper closure to determine what key we should use next when
52
// we are iterating, depending on whether we are iterating forwards or in
53
// reverse.
54
func (p paginator) nextKey() ([]byte, []byte) {
3✔
55
        if p.reversed {
3✔
UNCOV
56
                return p.cursor.Prev()
×
UNCOV
57
        }
×
58
        return p.cursor.Next()
3✔
59
}
60

61
// cursorStart gets the index key and value for the first item we are looking
62
// up, taking into account that we may be paginating in reverse. The index
63
// offset provided is *excusive* so we will start with the item after the offset
64
// for forwards queries, and the item before the index for backwards queries.
65
func (p paginator) cursorStart() ([]byte, []byte) {
3✔
66
        indexKey, indexValue := p.keyValueForIndex(p.indexOffset + 1)
3✔
67

3✔
68
        // If the query is specifying reverse iteration, then we must
3✔
69
        // handle a few offset cases.
3✔
70
        if p.reversed {
3✔
UNCOV
71
                switch {
×
72
                // This indicates the default case, where no offset was
73
                // specified. In that case we just start from the last
74
                // entry.
UNCOV
75
                case p.indexOffset == 0:
×
UNCOV
76
                        indexKey, indexValue = p.cursor.Last()
×
77

78
                // This indicates the offset being set to the very
79
                // first entry. Since there are no entries before
80
                // this offset, and the direction is reversed, we can
81
                // return without adding any invoices to the response.
UNCOV
82
                case p.indexOffset == 1:
×
UNCOV
83
                        return nil, nil
×
84

85
                // If we have been given an index offset that is beyond our last
86
                // index value, we just return the last indexed value in our set
87
                // since we are querying in reverse. We do not cover the case
88
                // where our index offset equals our last index value, because
89
                // index offset is exclusive, so we would want to start at the
90
                // value before our last index.
UNCOV
91
                case p.indexOffset > p.lastIndex():
×
UNCOV
92
                        return p.cursor.Last()
×
93

94
                // Otherwise we have an index offset which is within our set of
95
                // indexed keys, and we want to start at the item before our
96
                // offset. We seek to our index offset, then return the element
97
                // before it. We do this rather than p.indexOffset-1 to account
98
                // for indexes that have gaps.
UNCOV
99
                default:
×
UNCOV
100
                        p.keyValueForIndex(p.indexOffset)
×
UNCOV
101
                        indexKey, indexValue = p.cursor.Prev()
×
102
                }
103
        }
104

105
        return indexKey, indexValue
3✔
106
}
107

108
// query gets the start point for our index offset and iterates through keys
109
// in our index until we reach the total number of items required for the query
110
// or we run out of cursor values. This function takes a fetchAndAppend function
111
// which is responsible for looking up the entry at that index, adding the entry
112
// to its set of return items (if desired) and return a boolean which indicates
113
// whether the item was added. This is required to allow the paginator to
114
// determine when the response has the maximum number of required items.
115
func (p paginator) query(fetchAndAppend func(k, v []byte) (bool, error)) error {
3✔
116
        indexKey, indexValue := p.cursorStart()
3✔
117

3✔
118
        var totalItems int
3✔
119
        for ; indexKey != nil; indexKey, indexValue = p.nextKey() {
6✔
120
                // If our current return payload exceeds the max number
3✔
121
                // of invoices, then we'll exit now.
3✔
122
                if uint64(totalItems) >= p.totalItems {
3✔
UNCOV
123
                        break
×
124
                }
125

126
                added, err := fetchAndAppend(indexKey, indexValue)
3✔
127
                if err != nil {
3✔
128
                        return err
×
129
                }
×
130

131
                // If we added an item to our set in the latest fetch and append
132
                // we increment our total count.
133
                if added {
6✔
134
                        totalItems++
3✔
135
                }
3✔
136
        }
137

138
        return nil
3✔
139
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc