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

lightningnetwork / lnd / 15736109134

18 Jun 2025 02:46PM UTC coverage: 58.197% (-10.1%) from 68.248%
15736109134

Pull #9752

github

web-flow
Merge d2634a68c into 31c74f20f
Pull Request #9752: routerrpc: reject payment to invoice that don't have payment secret or blinded paths

6 of 13 new or added lines in 2 files covered. (46.15%)

28331 existing lines in 455 files now uncovered.

97860 of 168153 relevant lines covered (58.2%)

1.81 hits per line

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

79.0
/watchtower/wtclient/candidate_iterator.go
1
package wtclient
2

3
import (
4
        "container/list"
5
        "net"
6
        "sync"
7

8
        "github.com/lightningnetwork/lnd/watchtower/wtdb"
9
)
10

11
// TowerCandidateIterator provides an abstraction for iterating through possible
12
// watchtower addresses when attempting to create a new session.
13
type TowerCandidateIterator interface {
14
        // AddCandidate adds a new candidate tower to the iterator. If the
15
        // candidate already exists, then any new addresses are added to it.
16
        AddCandidate(*Tower)
17

18
        // RemoveCandidate removes an existing candidate tower from the
19
        // iterator. An optional address can be provided to indicate a stale
20
        // tower address to remove it. If it isn't provided, then the tower is
21
        // completely removed from the iterator.
22
        RemoveCandidate(wtdb.TowerID, net.Addr) error
23

24
        // IsActive determines whether a given tower is exists within the
25
        // iterator.
26
        IsActive(wtdb.TowerID) bool
27

28
        // Reset clears any internal iterator state, making previously taken
29
        // candidates available as long as they remain in the set.
30
        Reset() error
31

32
        // GetTower gets the tower with the given ID from the iterator. If no
33
        // such tower is found then ErrTowerNotInIterator is returned.
34
        GetTower(id wtdb.TowerID) (*Tower, error)
35

36
        // Next returns the next candidate tower. The iterator is not required
37
        // to return results in any particular order.  If no more candidates are
38
        // available, ErrTowerCandidatesExhausted is returned.
39
        Next() (*Tower, error)
40
}
41

42
// towerListIterator is a linked-list backed TowerCandidateIterator.
43
type towerListIterator struct {
44
        mu            sync.Mutex
45
        queue         *list.List
46
        nextCandidate *list.Element
47
        candidates    map[wtdb.TowerID]*Tower
48
}
49

50
// Compile-time constraint to ensure *towerListIterator implements the
51
// TowerCandidateIterator interface.
52
var _ TowerCandidateIterator = (*towerListIterator)(nil)
53

54
// newTowerListIterator initializes a new towerListIterator from a variadic list
55
// of lnwire.NetAddresses.
56
func newTowerListIterator(candidates ...*Tower) *towerListIterator {
3✔
57
        iter := &towerListIterator{
3✔
58
                queue:      list.New(),
3✔
59
                candidates: make(map[wtdb.TowerID]*Tower),
3✔
60
        }
3✔
61

3✔
62
        for _, candidate := range candidates {
3✔
UNCOV
63
                iter.queue.PushBack(candidate.ID)
×
UNCOV
64
                iter.candidates[candidate.ID] = candidate
×
UNCOV
65
        }
×
66
        iter.Reset()
3✔
67

3✔
68
        return iter
3✔
69
}
70

71
// Reset clears the iterators state, and makes the address at the front of the
72
// list the next item to be returned..
73
func (t *towerListIterator) Reset() error {
3✔
74
        t.mu.Lock()
3✔
75
        defer t.mu.Unlock()
3✔
76

3✔
77
        // Reset the next candidate to the front of the linked-list.
3✔
78
        t.nextCandidate = t.queue.Front()
3✔
79

3✔
80
        return nil
3✔
81
}
3✔
82

83
// GetTower gets the tower with the given ID from the iterator. If no such tower
84
// is found then ErrTowerNotInIterator is returned.
85
func (t *towerListIterator) GetTower(id wtdb.TowerID) (*Tower, error) {
3✔
86
        t.mu.Lock()
3✔
87
        defer t.mu.Unlock()
3✔
88

3✔
89
        tower, ok := t.candidates[id]
3✔
90
        if !ok {
3✔
UNCOV
91
                return nil, ErrTowerNotInIterator
×
UNCOV
92
        }
×
93

94
        return tower, nil
3✔
95
}
96

97
// Next returns the next candidate tower. This iterator will always return
98
// candidates in the order given when the iterator was instantiated.  If no more
99
// candidates are available, ErrTowerCandidatesExhausted is returned.
100
func (t *towerListIterator) Next() (*Tower, error) {
3✔
101
        t.mu.Lock()
3✔
102
        defer t.mu.Unlock()
3✔
103

3✔
104
        for t.nextCandidate != nil {
6✔
105
                // Propose the tower at the front of the list.
3✔
106
                towerID := t.nextCandidate.Value.(wtdb.TowerID)
3✔
107

3✔
108
                // Check whether this tower is still considered a candidate. If
3✔
109
                // it's not, we'll proceed to the next.
3✔
110
                tower, ok := t.candidates[towerID]
3✔
111
                if !ok {
6✔
112
                        nextCandidate := t.nextCandidate.Next()
3✔
113
                        t.queue.Remove(t.nextCandidate)
3✔
114
                        t.nextCandidate = nextCandidate
3✔
115
                        continue
3✔
116
                }
117

118
                // Set the next candidate to the subsequent element.
119
                t.nextCandidate = t.nextCandidate.Next()
3✔
120
                return tower, nil
3✔
121
        }
122

123
        return nil, ErrTowerCandidatesExhausted
3✔
124
}
125

126
// AddCandidate adds a new candidate tower to the iterator. If the candidate
127
// already exists, then any new addresses are added to it.
128
func (t *towerListIterator) AddCandidate(candidate *Tower) {
3✔
129
        t.mu.Lock()
3✔
130
        defer t.mu.Unlock()
3✔
131

3✔
132
        if tower, ok := t.candidates[candidate.ID]; !ok {
6✔
133
                t.queue.PushBack(candidate.ID)
3✔
134
                t.candidates[candidate.ID] = candidate
3✔
135

3✔
136
                // If we've reached the end of our queue, then this candidate
3✔
137
                // will become the next.
3✔
138
                if t.nextCandidate == nil {
6✔
139
                        t.nextCandidate = t.queue.Back()
3✔
140
                }
3✔
UNCOV
141
        } else {
×
UNCOV
142
                candidate.Addresses.Reset()
×
UNCOV
143
                firstAddr := candidate.Addresses.Peek()
×
UNCOV
144
                tower.Addresses.Add(firstAddr)
×
UNCOV
145
                for {
×
UNCOV
146
                        next, err := candidate.Addresses.Next()
×
UNCOV
147
                        if err != nil {
×
UNCOV
148
                                candidate.Addresses.Reset()
×
UNCOV
149
                                break
×
150
                        }
151

UNCOV
152
                        tower.Addresses.Add(next)
×
153
                }
154
        }
155
}
156

157
// RemoveCandidate removes an existing candidate tower from the iterator. An
158
// optional address can be provided to indicate a stale tower address to remove
159
// it. If it isn't provided, then the tower is completely removed from the
160
// iterator.
161
func (t *towerListIterator) RemoveCandidate(candidate wtdb.TowerID,
162
        addr net.Addr) error {
3✔
163

3✔
164
        t.mu.Lock()
3✔
165
        defer t.mu.Unlock()
3✔
166

3✔
167
        tower, ok := t.candidates[candidate]
3✔
168
        if !ok {
6✔
169
                return nil
3✔
170
        }
3✔
171
        if addr != nil {
3✔
UNCOV
172
                err := tower.Addresses.Remove(addr)
×
UNCOV
173
                if err != nil {
×
UNCOV
174
                        return err
×
UNCOV
175
                }
×
176
        } else {
3✔
177
                if tower.Addresses.HasLocked() {
3✔
178
                        return ErrAddrInUse
×
179
                }
×
180

181
                delete(t.candidates, candidate)
3✔
182
        }
183

184
        return nil
3✔
185
}
186

187
// IsActive determines whether a given tower is exists within the iterator.
188
func (t *towerListIterator) IsActive(tower wtdb.TowerID) bool {
3✔
189
        t.mu.Lock()
3✔
190
        defer t.mu.Unlock()
3✔
191

3✔
192
        _, ok := t.candidates[tower]
3✔
193
        return ok
3✔
194
}
3✔
195

196
// TODO(conner): implement graph-backed candidate iterator for public towers.
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