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

lightningnetwork / lnd / 14193549836

01 Apr 2025 10:40AM UTC coverage: 69.046% (+0.007%) from 69.039%
14193549836

Pull #9665

github

web-flow
Merge e8825f209 into b01f4e514
Pull Request #9665: kvdb: bump etcd libs to v3.5.12

133439 of 193262 relevant lines covered (69.05%)

22119.45 hits per line

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

100.0
/watchtower/wtclient/addr_iterator.go
1
package wtclient
2

3
import (
4
        "container/list"
5
        "errors"
6
        "fmt"
7
        "net"
8
        "sync"
9

10
        "github.com/lightningnetwork/lnd/watchtower/wtdb"
11
)
12

13
var (
14
        // ErrAddressesExhausted signals that a addressIterator has cycled
15
        // through all available addresses.
16
        ErrAddressesExhausted = errors.New("exhausted all addresses")
17

18
        // ErrAddrInUse indicates that an address is locked and cannot be
19
        // removed from the addressIterator.
20
        ErrAddrInUse = errors.New("address in use")
21
)
22

23
// AddressIterator handles iteration over a list of addresses. It strictly
24
// disallows the list of addresses it holds to be empty. It also allows callers
25
// to place locks on certain addresses in order to prevent other callers from
26
// removing the addresses in question from the iterator.
27
type AddressIterator interface {
28
        // Next returns the next candidate address. This iterator will always
29
        // return candidates in the order given when the iterator was
30
        // instantiated. If no more candidates are available,
31
        // ErrAddressesExhausted is returned.
32
        Next() (net.Addr, error)
33

34
        // NextAndLock does the same as described for Next, and it also places a
35
        // lock on the returned address so that the address can not be removed
36
        // until the lock on it has been released via ReleaseLock.
37
        NextAndLock() (net.Addr, error)
38

39
        // Peek returns the currently selected address in the iterator. If the
40
        // end of the iterator has been reached then it is reset and the first
41
        // item in the iterator is returned. Since the AddressIterator will
42
        // never have an empty address list, this function will never return a
43
        // nil value.
44
        Peek() net.Addr
45

46
        // PeekAndLock does the same as described for Peek, and it also places
47
        // a lock on the returned address so that the address can not be removed
48
        // until the lock on it has been released via ReleaseLock.
49
        PeekAndLock() net.Addr
50

51
        // ReleaseLock releases the lock held on the given address.
52
        ReleaseLock(addr net.Addr)
53

54
        // Add adds a new address to the iterator.
55
        Add(addr net.Addr)
56

57
        // Remove removes an existing address from the iterator. It disallows
58
        // the address from being removed if it is the last address in the
59
        // iterator or if there is currently a lock on the address.
60
        Remove(addr net.Addr) error
61

62
        // HasLocked returns true if the addressIterator has any locked
63
        // addresses.
64
        HasLocked() bool
65

66
        // GetAll returns a copy of all the addresses in the iterator.
67
        GetAll() []net.Addr
68

69
        // Reset clears the iterators state, and makes the address at the front
70
        // of the list the next item to be returned.
71
        Reset()
72

73
        // Copy constructs a new AddressIterator that has the same addresses
74
        // as this iterator.
75
        //
76
        // NOTE that the address locks are not expected to be copied.
77
        Copy() AddressIterator
78
}
79

80
// A compile-time check to ensure that addressIterator implements the
81
// AddressIterator interface.
82
var _ AddressIterator = (*addressIterator)(nil)
83

84
// addressIterator is a linked-list implementation of an AddressIterator.
85
type addressIterator struct {
86
        mu             sync.Mutex
87
        addrList       *list.List
88
        currentTopAddr *list.Element
89
        candidates     map[string]*candidateAddr
90
        totalLockCount int
91
}
92

93
type candidateAddr struct {
94
        addr     net.Addr
95
        numLocks int
96
}
97

98
// newAddressIterator constructs a new addressIterator.
99
func newAddressIterator(addrs ...net.Addr) (*addressIterator, error) {
1,445✔
100
        if len(addrs) == 0 {
1,449✔
101
                return nil, fmt.Errorf("must have at least one address")
4✔
102
        }
4✔
103

104
        iter := &addressIterator{
1,441✔
105
                addrList:   list.New(),
1,441✔
106
                candidates: make(map[string]*candidateAddr),
1,441✔
107
        }
1,441✔
108

1,441✔
109
        for _, addr := range addrs {
5,233✔
110
                addrID := addr.String()
3,792✔
111
                iter.addrList.PushBack(addrID)
3,792✔
112
                iter.candidates[addrID] = &candidateAddr{addr: addr}
3,792✔
113
        }
3,792✔
114
        iter.Reset()
1,441✔
115

1,441✔
116
        return iter, nil
1,441✔
117
}
118

119
// Reset clears the iterators state, and makes the address at the front of the
120
// list the next item to be returned.
121
//
122
// NOTE: This is part of the AddressIterator interface.
123
func (a *addressIterator) Reset() {
1,778✔
124
        a.mu.Lock()
1,778✔
125
        defer a.mu.Unlock()
1,778✔
126

1,778✔
127
        a.unsafeReset()
1,778✔
128
}
1,778✔
129

130
// unsafeReset clears the iterator state and makes the address at the front of
131
// the list the next item to be returned.
132
//
133
// NOTE: this method is not thread safe and so should only be called if the
134
// appropriate mutex is being held.
135
func (a *addressIterator) unsafeReset() {
2,066✔
136
        // Reset the next candidate to the front of the linked-list.
2,066✔
137
        a.currentTopAddr = a.addrList.Front()
2,066✔
138
}
2,066✔
139

140
// Next returns the next candidate address. This iterator will always return
141
// candidates in the order given when the iterator was instantiated. If no more
142
// candidates are available, ErrAddressesExhausted is returned.
143
//
144
// NOTE: This is part of the AddressIterator interface.
145
func (a *addressIterator) Next() (net.Addr, error) {
3,952✔
146
        return a.next(false)
3,952✔
147
}
3,952✔
148

149
// NextAndLock does the same as described for Next, and it also places a lock on
150
// the returned address so that the address can not be removed until the lock on
151
// it has been released via ReleaseLock.
152
//
153
// NOTE: This is part of the AddressIterator interface.
154
func (a *addressIterator) NextAndLock() (net.Addr, error) {
806✔
155
        return a.next(true)
806✔
156
}
806✔
157

158
// next returns the next candidate address. This iterator will always return
159
// candidates in the order given when the iterator was instantiated. If no more
160
// candidates are available, ErrAddressesExhausted is returned.
161
func (a *addressIterator) next(lock bool) (net.Addr, error) {
4,758✔
162
        a.mu.Lock()
4,758✔
163
        defer a.mu.Unlock()
4,758✔
164

4,758✔
165
        // In-case currentTopAddr is nil (meaning that Reset has not yet been
4,758✔
166
        // called), return an error indicating this.
4,758✔
167
        if a.currentTopAddr == nil {
7,973✔
168
                return nil, ErrAddressesExhausted
3,215✔
169
        }
3,215✔
170

171
        // Set the next candidate to the subsequent element. If we are at the
172
        // end of the address list, this could mean setting currentTopAddr to
173
        // nil.
174
        a.currentTopAddr = a.currentTopAddr.Next()
1,543✔
175

1,543✔
176
        for a.currentTopAddr != nil {
2,766✔
177
                // Propose the address at the front of the list.
1,223✔
178
                addrID := a.currentTopAddr.Value.(string)
1,223✔
179

1,223✔
180
                // Check whether this address is still considered a candidate.
1,223✔
181
                // If it's not, we'll proceed to the next.
1,223✔
182
                candidate, ok := a.candidates[addrID]
1,223✔
183

1,223✔
184
                // If the address cannot be found in the candidate set, then
1,223✔
185
                // this must mean that the Remove method was called for the
1,223✔
186
                // address. The Remove method would have checked that the
1,223✔
187
                // address is not the last one in the iterator and that it has
1,223✔
188
                // no locks on it. It is therefor safe to remove.
1,223✔
189
                if !ok {
1,293✔
190
                        // Grab the next address candidate. This might be nil
70✔
191
                        // if the iterator is on the last item in the list.
70✔
192
                        nextCandidate := a.currentTopAddr.Next()
70✔
193

70✔
194
                        // Remove the address from the list that is no longer
70✔
195
                        // in the candidate set.
70✔
196
                        a.addrList.Remove(a.currentTopAddr)
70✔
197

70✔
198
                        // Set the current top to the next candidate. This might
70✔
199
                        // mean setting it to nil if the iterator is on its last
70✔
200
                        // item in which case the loop will be exited and an
70✔
201
                        // ErrAddressesExhausted exhausted error will be
70✔
202
                        // returned.
70✔
203
                        a.currentTopAddr = nextCandidate
70✔
204
                        continue
70✔
205
                }
206

207
                if lock {
1,557✔
208
                        candidate.numLocks++
404✔
209
                        a.totalLockCount++
404✔
210
                }
404✔
211

212
                return candidate.addr, nil
1,153✔
213
        }
214

215
        return nil, ErrAddressesExhausted
390✔
216
}
217

218
// Peek returns the currently selected address in the iterator. If the end of
219
// the list has been reached then the iterator is reset and the first item in
220
// the list is returned. Since the addressIterator will never have an empty
221
// address list, this function will never return a nil value.
222
//
223
// NOTE: This is part of the AddressIterator interface.
224
func (a *addressIterator) Peek() net.Addr {
859✔
225
        return a.peek(false)
859✔
226
}
859✔
227

228
// PeekAndLock does the same as described for Peek, and it also places a lock on
229
// the returned address so that the address can not be removed until the lock
230
// on it has been released via ReleaseLock.
231
//
232
// NOTE: This is part of the AddressIterator interface.
233
func (a *addressIterator) PeekAndLock() net.Addr {
576✔
234
        return a.peek(true)
576✔
235
}
576✔
236

237
// peek returns the currently selected address in the iterator. If the end of
238
// the list has been reached then the iterator is reset and the first item in
239
// the list is returned. Since the addressIterator will never have an empty
240
// address list, this function will never return a nil value. If lock is set to
241
// true, the address will be locked for removal until ReleaseLock has been
242
// called for the address.
243
func (a *addressIterator) peek(lock bool) net.Addr {
1,432✔
244
        a.mu.Lock()
1,432✔
245
        defer a.mu.Unlock()
1,432✔
246

1,432✔
247
        for {
2,899✔
248
                // If currentTopAddr is nil, it means we have reached the end of
1,467✔
249
                // the list, so we reset it here. The iterator always has at
1,467✔
250
                // least one address, so we can be sure that currentTopAddr will
1,467✔
251
                // be non-nil after calling reset here.
1,467✔
252
                if a.currentTopAddr == nil {
1,755✔
253
                        a.unsafeReset()
288✔
254
                }
288✔
255

256
                addrID := a.currentTopAddr.Value.(string)
1,467✔
257
                candidate, ok := a.candidates[addrID]
1,467✔
258

1,467✔
259
                // If the address cannot be found in the candidate set, then
1,467✔
260
                // this must mean that the Remove method was called for the
1,467✔
261
                // address. The Remove method would have checked that the
1,467✔
262
                // address is not the last one in the iterator and that it has
1,467✔
263
                // no locks on it. It is therefor safe to remove.
1,467✔
264
                if !ok {
1,502✔
265
                        // Grab the next address candidate. This might be nil
35✔
266
                        // if the iterator is on the last item in the list.
35✔
267
                        nextCandidate := a.currentTopAddr.Next()
35✔
268

35✔
269
                        // Remove the address from the list that is no longer
35✔
270
                        // in the candidate set.
35✔
271
                        a.addrList.Remove(a.currentTopAddr)
35✔
272

35✔
273
                        // Set the current top to the next candidate. This might
35✔
274
                        // mean setting it to nil if the iterator is on its last
35✔
275
                        // item but this will be reset at the top of the for
35✔
276
                        // loop.
35✔
277
                        a.currentTopAddr = nextCandidate
35✔
278
                        continue
35✔
279
                }
280

281
                if lock {
2,008✔
282
                        candidate.numLocks++
576✔
283
                        a.totalLockCount++
576✔
284
                }
576✔
285

286
                return candidate.addr
1,432✔
287
        }
288
}
289

290
// ReleaseLock releases the lock held on the given address.
291
//
292
// NOTE: This is part of the AddressIterator interface.
293
func (a *addressIterator) ReleaseLock(addr net.Addr) {
758✔
294
        a.mu.Lock()
758✔
295
        defer a.mu.Unlock()
758✔
296

758✔
297
        candidateAddr, ok := a.candidates[addr.String()]
758✔
298
        if !ok {
1,013✔
299
                return
255✔
300
        }
255✔
301

302
        if candidateAddr.numLocks == 0 {
758✔
303
                return
255✔
304
        }
255✔
305

306
        candidateAddr.numLocks--
248✔
307
        a.totalLockCount--
248✔
308
}
309

310
// Add adds a new address to the iterator.
311
//
312
// NOTE: This is part of the AddressIterator interface.
313
func (a *addressIterator) Add(addr net.Addr) {
414✔
314
        a.mu.Lock()
414✔
315
        defer a.mu.Unlock()
414✔
316

414✔
317
        if _, ok := a.candidates[addr.String()]; ok {
688✔
318
                return
274✔
319
        }
274✔
320

321
        a.addrList.PushBack(addr.String())
140✔
322
        a.candidates[addr.String()] = &candidateAddr{addr: addr}
140✔
323

140✔
324
        // If we've reached the end of our queue, then this candidate
140✔
325
        // will become the next.
140✔
326
        if a.currentTopAddr == nil {
156✔
327
                a.currentTopAddr = a.addrList.Back()
16✔
328
        }
16✔
329
}
330

331
// Remove removes an existing address from the iterator. It disallows the
332
// address from being removed if it is the last address in the iterator or if
333
// there is currently a lock on the address.
334
//
335
// NOTE: This is part of the AddressIterator interface.
336
func (a *addressIterator) Remove(addr net.Addr) error {
908✔
337
        a.mu.Lock()
908✔
338
        defer a.mu.Unlock()
908✔
339

908✔
340
        candidate, ok := a.candidates[addr.String()]
908✔
341
        if !ok {
1,162✔
342
                return nil
254✔
343
        }
254✔
344

345
        if len(a.candidates) == 1 {
999✔
346
                return wtdb.ErrLastTowerAddr
345✔
347
        }
345✔
348

349
        if candidate.numLocks > 0 {
575✔
350
                return ErrAddrInUse
266✔
351
        }
266✔
352

353
        delete(a.candidates, addr.String())
43✔
354
        return nil
43✔
355
}
356

357
// HasLocked returns true if the addressIterator has any locked addresses.
358
//
359
// NOTE: This is part of the AddressIterator interface.
360
func (a *addressIterator) HasLocked() bool {
290✔
361
        a.mu.Lock()
290✔
362
        defer a.mu.Unlock()
290✔
363

290✔
364
        return a.totalLockCount > 0
290✔
365
}
290✔
366

367
// GetAll returns a copy of all the addresses in the iterator.
368
//
369
// NOTE: This is part of the AddressIterator interface.
370
func (a *addressIterator) GetAll() []net.Addr {
646✔
371
        a.mu.Lock()
646✔
372
        defer a.mu.Unlock()
646✔
373

646✔
374
        return a.getAllUnsafe()
646✔
375
}
646✔
376

377
// Copy constructs a new AddressIterator that has the same addresses
378
// as this iterator.
379
//
380
// NOTE that the address locks will not be copied.
381
func (a *addressIterator) Copy() AddressIterator {
1,120✔
382
        a.mu.Lock()
1,120✔
383
        defer a.mu.Unlock()
1,120✔
384

1,120✔
385
        addrs := a.getAllUnsafe()
1,120✔
386

1,120✔
387
        // Since newAddressIterator will only ever return an error if it is
1,120✔
388
        // initialised with zero addresses, we can ignore the error here since
1,120✔
389
        // we are initialising it with the set of addresses of this
1,120✔
390
        // addressIterator which is by definition a non-empty list.
1,120✔
391
        iter, _ := newAddressIterator(addrs...)
1,120✔
392

1,120✔
393
        return iter
1,120✔
394
}
1,120✔
395

396
// getAllUnsafe returns a copy of all the addresses in the iterator.
397
//
398
// NOTE: this method is not thread safe and so must only be called once the
399
// addressIterator mutex is already being held.
400
func (a *addressIterator) getAllUnsafe() []net.Addr {
1,766✔
401
        var addrs []net.Addr
1,766✔
402
        cursor := a.addrList.Front()
1,766✔
403

1,766✔
404
        for cursor != nil {
4,867✔
405
                addrID := cursor.Value.(string)
3,101✔
406

3,101✔
407
                addr, ok := a.candidates[addrID]
3,101✔
408
                if !ok {
3,601✔
409
                        cursor = cursor.Next()
500✔
410
                        continue
500✔
411
                }
412

413
                addrs = append(addrs, addr.addr)
2,601✔
414
                cursor = cursor.Next()
2,601✔
415
        }
416

417
        return addrs
1,766✔
418
}
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