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

lightningnetwork / lnd / 13980275562

20 Mar 2025 10:06PM UTC coverage: 58.6% (-10.2%) from 68.789%
13980275562

Pull #9623

github

web-flow
Merge b9b960345 into 09b674508
Pull Request #9623: Size msg test msg

0 of 1518 new or added lines in 42 files covered. (0.0%)

26603 existing lines in 443 files now uncovered.

96807 of 165200 relevant lines covered (58.6%)

1.82 hits per line

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

45.93
/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) {
3✔
100
        if len(addrs) == 0 {
3✔
UNCOV
101
                return nil, fmt.Errorf("must have at least one address")
×
UNCOV
102
        }
×
103

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

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

3✔
116
        return iter, nil
3✔
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() {
3✔
124
        a.mu.Lock()
3✔
125
        defer a.mu.Unlock()
3✔
126

3✔
127
        a.unsafeReset()
3✔
128
}
3✔
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() {
3✔
136
        // Reset the next candidate to the front of the linked-list.
3✔
137
        a.currentTopAddr = a.addrList.Front()
3✔
138
}
3✔
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.
UNCOV
145
func (a *addressIterator) Next() (net.Addr, error) {
×
UNCOV
146
        return a.next(false)
×
UNCOV
147
}
×
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.
UNCOV
154
func (a *addressIterator) NextAndLock() (net.Addr, error) {
×
UNCOV
155
        return a.next(true)
×
UNCOV
156
}
×
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.
UNCOV
161
func (a *addressIterator) next(lock bool) (net.Addr, error) {
×
UNCOV
162
        a.mu.Lock()
×
UNCOV
163
        defer a.mu.Unlock()
×
UNCOV
164

×
UNCOV
165
        // In-case currentTopAddr is nil (meaning that Reset has not yet been
×
UNCOV
166
        // called), return an error indicating this.
×
UNCOV
167
        if a.currentTopAddr == nil {
×
UNCOV
168
                return nil, ErrAddressesExhausted
×
UNCOV
169
        }
×
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.
UNCOV
174
        a.currentTopAddr = a.currentTopAddr.Next()
×
UNCOV
175

×
UNCOV
176
        for a.currentTopAddr != nil {
×
UNCOV
177
                // Propose the address at the front of the list.
×
UNCOV
178
                addrID := a.currentTopAddr.Value.(string)
×
UNCOV
179

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

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

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

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

UNCOV
207
                if lock {
×
UNCOV
208
                        candidate.numLocks++
×
UNCOV
209
                        a.totalLockCount++
×
UNCOV
210
                }
×
211

UNCOV
212
                return candidate.addr, nil
×
213
        }
214

UNCOV
215
        return nil, ErrAddressesExhausted
×
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 {
3✔
225
        return a.peek(false)
3✔
226
}
3✔
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 {
3✔
234
        return a.peek(true)
3✔
235
}
3✔
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 {
3✔
244
        a.mu.Lock()
3✔
245
        defer a.mu.Unlock()
3✔
246

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

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

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

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

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

281
                if lock {
6✔
282
                        candidate.numLocks++
3✔
283
                        a.totalLockCount++
3✔
284
                }
3✔
285

286
                return candidate.addr
3✔
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) {
3✔
294
        a.mu.Lock()
3✔
295
        defer a.mu.Unlock()
3✔
296

3✔
297
        candidateAddr, ok := a.candidates[addr.String()]
3✔
298
        if !ok {
3✔
UNCOV
299
                return
×
UNCOV
300
        }
×
301

302
        if candidateAddr.numLocks == 0 {
3✔
UNCOV
303
                return
×
UNCOV
304
        }
×
305

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

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

×
UNCOV
317
        if _, ok := a.candidates[addr.String()]; ok {
×
UNCOV
318
                return
×
UNCOV
319
        }
×
320

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

×
UNCOV
324
        // If we've reached the end of our queue, then this candidate
×
UNCOV
325
        // will become the next.
×
UNCOV
326
        if a.currentTopAddr == nil {
×
UNCOV
327
                a.currentTopAddr = a.addrList.Back()
×
UNCOV
328
        }
×
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.
UNCOV
336
func (a *addressIterator) Remove(addr net.Addr) error {
×
UNCOV
337
        a.mu.Lock()
×
UNCOV
338
        defer a.mu.Unlock()
×
UNCOV
339

×
UNCOV
340
        candidate, ok := a.candidates[addr.String()]
×
UNCOV
341
        if !ok {
×
UNCOV
342
                return nil
×
UNCOV
343
        }
×
344

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

UNCOV
349
        if candidate.numLocks > 0 {
×
UNCOV
350
                return ErrAddrInUse
×
UNCOV
351
        }
×
352

UNCOV
353
        delete(a.candidates, addr.String())
×
UNCOV
354
        return nil
×
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 {
3✔
361
        a.mu.Lock()
3✔
362
        defer a.mu.Unlock()
3✔
363

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

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

×
UNCOV
374
        return a.getAllUnsafe()
×
UNCOV
375
}
×
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 {
3✔
382
        a.mu.Lock()
3✔
383
        defer a.mu.Unlock()
3✔
384

3✔
385
        addrs := a.getAllUnsafe()
3✔
386

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

3✔
393
        return iter
3✔
394
}
3✔
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 {
3✔
401
        var addrs []net.Addr
3✔
402
        cursor := a.addrList.Front()
3✔
403

3✔
404
        for cursor != nil {
6✔
405
                addrID := cursor.Value.(string)
3✔
406

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

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

417
        return addrs
3✔
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