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

lightningnetwork / lnd / 12312390362

13 Dec 2024 08:44AM UTC coverage: 57.458% (+8.5%) from 48.92%
12312390362

Pull #9343

github

ellemouton
fn: rework the ContextGuard and add tests

In this commit, the ContextGuard struct is re-worked such that the
context that its new main WithCtx method provides is cancelled in sync
with a parent context being cancelled or with it's quit channel being
cancelled. Tests are added to assert the behaviour. In order for the
close of the quit channel to be consistent with the cancelling of the
derived context, the quit channel _must_ be contained internal to the
ContextGuard so that callers are only able to close the channel via the
exposed Quit method which will then take care to first cancel any
derived context that depend on the quit channel before returning.
Pull Request #9343: fn: expand the ContextGuard and add tests

101853 of 177264 relevant lines covered (57.46%)

24972.93 hits per line

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

96.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 {
37✔
57
        iter := &towerListIterator{
37✔
58
                queue:      list.New(),
37✔
59
                candidates: make(map[wtdb.TowerID]*Tower),
37✔
60
        }
37✔
61

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

37✔
68
        return iter
37✔
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 {
169✔
74
        t.mu.Lock()
169✔
75
        defer t.mu.Unlock()
169✔
76

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

169✔
80
        return nil
169✔
81
}
169✔
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) {
6✔
86
        t.mu.Lock()
6✔
87
        defer t.mu.Unlock()
6✔
88

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

94
        return tower, nil
5✔
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) {
251✔
101
        t.mu.Lock()
251✔
102
        defer t.mu.Unlock()
251✔
103

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

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

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

123
        return nil, ErrTowerCandidatesExhausted
132✔
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) {
58✔
129
        t.mu.Lock()
58✔
130
        defer t.mu.Unlock()
58✔
131

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

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

152
                        tower.Addresses.Add(next)
3✔
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 {
16✔
163

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

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

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

184
        return nil
14✔
185
}
186

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

15✔
192
        _, ok := t.candidates[tower]
15✔
193
        return ok
15✔
194
}
15✔
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