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

lightningnetwork / lnd / 16683051882

01 Aug 2025 07:03PM UTC coverage: 54.949% (-12.1%) from 67.047%
16683051882

Pull #9455

github

web-flow
Merge 3f1f50be8 into 37523b6cb
Pull Request #9455: discovery+lnwire: add support for DNS host name in NodeAnnouncement msg

144 of 226 new or added lines in 7 files covered. (63.72%)

23852 existing lines in 290 files now uncovered.

108751 of 197912 relevant lines covered (54.95%)

22080.83 hits per line

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

70.82
/accessman.go
1
package lnd
2

3
import (
4
        "context"
5
        "fmt"
6
        "maps"
7
        "sync"
8

9
        "github.com/btcsuite/btcd/btcec/v2"
10
        "github.com/btcsuite/btclog/v2"
11
        "github.com/lightningnetwork/lnd/channeldb"
12
        "github.com/lightningnetwork/lnd/lnutils"
13
)
14

15
// accessMan is responsible for managing the server's access permissions.
16
type accessMan struct {
17
        cfg *accessManConfig
18

19
        // banScoreMtx is used for the server's ban tracking. If the server
20
        // mutex is also going to be locked, ensure that this is locked after
21
        // the server mutex.
22
        banScoreMtx sync.RWMutex
23

24
        // peerChanInfo is a mapping from remote public key to {bool, uint64}
25
        // where the bool indicates that we have an open/closed channel with the
26
        // peer and where the uint64 indicates the number of pending-open
27
        // channels we currently have with them. This mapping will be used to
28
        // determine access permissions for the peer. The map key is the
29
        // string-version of the serialized public key.
30
        //
31
        // NOTE: This MUST be accessed with the banScoreMtx held.
32
        peerChanInfo map[string]channeldb.ChanCount
33

34
        // peerScores stores each connected peer's access status. The map key
35
        // is the string-version of the serialized public key.
36
        //
37
        // NOTE: This MUST be accessed with the banScoreMtx held.
38
        //
39
        // TODO(yy): unify `peerScores` and `peerChanInfo` - there's no need to
40
        // create two maps tracking essentially the same info. `numRestricted`
41
        // can also be derived from `peerChanInfo`.
42
        peerScores map[string]peerSlotStatus
43

44
        // numRestricted tracks the number of peers with restricted access in
45
        // peerScores. This MUST be accessed with the banScoreMtx held.
46
        numRestricted int64
47
}
48

49
type accessManConfig struct {
50
        // initAccessPerms checks the channeldb for initial access permissions
51
        // and then populates the peerChanInfo and peerScores maps.
52
        initAccessPerms func() (map[string]channeldb.ChanCount, error)
53

54
        // shouldDisconnect determines whether we should disconnect a peer or
55
        // not.
56
        shouldDisconnect func(*btcec.PublicKey) (bool, error)
57

58
        // maxRestrictedSlots is the number of restricted slots we'll allocate.
59
        maxRestrictedSlots int64
60
}
61

62
func newAccessMan(cfg *accessManConfig) (*accessMan, error) {
11✔
63
        a := &accessMan{
11✔
64
                cfg:          cfg,
11✔
65
                peerChanInfo: make(map[string]channeldb.ChanCount),
11✔
66
                peerScores:   make(map[string]peerSlotStatus),
11✔
67
        }
11✔
68

11✔
69
        counts, err := a.cfg.initAccessPerms()
11✔
70
        if err != nil {
11✔
71
                return nil, err
×
72
        }
×
73

74
        // We'll populate the server's peerChanInfo map with the counts fetched
75
        // via initAccessPerms. Also note that we haven't yet connected to the
76
        // peers.
77
        maps.Copy(a.peerChanInfo, counts)
11✔
78

11✔
79
        acsmLog.Info("Access Manager initialized")
11✔
80

11✔
81
        return a, nil
11✔
82
}
83

84
// hasPeer checks whether a given peer already exists in the internal maps.
85
func (a *accessMan) hasPeer(ctx context.Context,
86
        pub string) (peerAccessStatus, bool) {
22✔
87

22✔
88
        // Lock banScoreMtx for reading so that we can read the banning maps
22✔
89
        // below.
22✔
90
        a.banScoreMtx.RLock()
22✔
91
        defer a.banScoreMtx.RUnlock()
22✔
92

22✔
93
        count, found := a.peerChanInfo[pub]
22✔
94
        if found {
38✔
95
                if count.HasOpenOrClosedChan {
23✔
96
                        acsmLog.DebugS(ctx, "Peer has open/closed channel, "+
7✔
97
                                "assigning protected access")
7✔
98

7✔
99
                        // Exit early if the peer is no longer restricted.
7✔
100
                        return peerStatusProtected, true
7✔
101
                }
7✔
102

103
                if count.PendingOpenCount != 0 {
13✔
104
                        acsmLog.DebugS(ctx, "Peer has pending channel(s), "+
4✔
105
                                "assigning temporary access")
4✔
106

4✔
107
                        // Exit early if the peer is no longer restricted.
4✔
108
                        return peerStatusTemporary, true
4✔
109
                }
4✔
110

111
                return peerStatusRestricted, true
5✔
112
        }
113

114
        // Check if the peer is found in the scores map.
115
        status, found := a.peerScores[pub]
6✔
116
        if found {
8✔
117
                acsmLog.DebugS(ctx, "Peer already has access", "access",
2✔
118
                        status.state)
2✔
119

2✔
120
                return status.state, true
2✔
121
        }
2✔
122

123
        return peerStatusRestricted, false
4✔
124
}
125

126
// assignPeerPerms assigns a new peer its permissions. This does not track the
127
// access in the maps. This is intentional.
128
func (a *accessMan) assignPeerPerms(remotePub *btcec.PublicKey) (
129
        peerAccessStatus, error) {
17✔
130

17✔
131
        ctx := btclog.WithCtx(
17✔
132
                context.TODO(), lnutils.LogPubKey("peer", remotePub),
17✔
133
        )
17✔
134

17✔
135
        peerMapKey := string(remotePub.SerializeCompressed())
17✔
136

17✔
137
        acsmLog.DebugS(ctx, "Assigning permissions")
17✔
138

17✔
139
        // Default is restricted unless the below filters say otherwise.
17✔
140
        access, peerExist := a.hasPeer(ctx, peerMapKey)
17✔
141

17✔
142
        // Exit early if the peer is not restricted.
17✔
143
        if access != peerStatusRestricted {
27✔
144
                return access, nil
10✔
145
        }
10✔
146

147
        // If we are here, it means the peer has peerStatusRestricted.
148
        //
149
        // Check whether this peer is banned.
150
        shouldDisconnect, err := a.cfg.shouldDisconnect(remotePub)
7✔
151
        if err != nil {
7✔
152
                acsmLog.ErrorS(ctx, "Error checking disconnect status", err)
×
153

×
154
                // Access is restricted here.
×
155
                return access, err
×
156
        }
×
157

158
        if shouldDisconnect {
8✔
159
                acsmLog.WarnS(ctx, "Peer is banned, assigning restricted access",
1✔
160
                        ErrGossiperBan)
1✔
161

1✔
162
                // Access is restricted here.
1✔
163
                return access, ErrGossiperBan
1✔
164
        }
1✔
165

166
        // If we've reached this point and access hasn't changed from
167
        // restricted, then we need to check if we even have a slot for this
168
        // peer.
169
        acsmLog.DebugS(ctx, "Peer has no channels, assigning restricted access")
6✔
170

6✔
171
        // If this is an existing peer, there's no need to check for slot limit.
6✔
172
        if peerExist {
9✔
173
                acsmLog.DebugS(ctx, "Skipped slot check for existing peer")
3✔
174
                return access, nil
3✔
175
        }
3✔
176

177
        a.banScoreMtx.RLock()
3✔
178
        defer a.banScoreMtx.RUnlock()
3✔
179

3✔
180
        if a.numRestricted >= a.cfg.maxRestrictedSlots {
4✔
181
                acsmLog.WarnS(ctx, "No more restricted slots available, "+
1✔
182
                        "denying peer", ErrNoMoreRestrictedAccessSlots,
1✔
183
                        "num_restricted", a.numRestricted, "max_restricted",
1✔
184
                        a.cfg.maxRestrictedSlots)
1✔
185

1✔
186
                return access, ErrNoMoreRestrictedAccessSlots
1✔
187
        }
1✔
188

189
        return access, nil
2✔
190
}
191

192
// newPendingOpenChan is called after the pending-open channel has been
193
// committed to the database. This may transition a restricted-access peer to a
194
// temporary-access peer.
195
func (a *accessMan) newPendingOpenChan(remotePub *btcec.PublicKey) error {
1✔
196
        a.banScoreMtx.Lock()
1✔
197
        defer a.banScoreMtx.Unlock()
1✔
198

1✔
199
        ctx := btclog.WithCtx(
1✔
200
                context.TODO(), lnutils.LogPubKey("peer", remotePub),
1✔
201
        )
1✔
202

1✔
203
        acsmLog.DebugS(ctx, "Processing new pending open channel")
1✔
204

1✔
205
        peerMapKey := string(remotePub.SerializeCompressed())
1✔
206

1✔
207
        // Fetch the peer's access status from peerScores.
1✔
208
        status, found := a.peerScores[peerMapKey]
1✔
209
        if !found {
1✔
210
                acsmLog.ErrorS(ctx, "Peer score not found", ErrNoPeerScore)
×
211

×
212
                // If we didn't find the peer, we'll return an error.
×
213
                return ErrNoPeerScore
×
214
        }
×
215

216
        switch status.state {
1✔
UNCOV
217
        case peerStatusProtected:
×
UNCOV
218
                acsmLog.DebugS(ctx, "Peer already protected, no change")
×
UNCOV
219

×
UNCOV
220
                // If this peer's access status is protected, we don't need to
×
UNCOV
221
                // do anything.
×
UNCOV
222
                return nil
×
223

UNCOV
224
        case peerStatusTemporary:
×
UNCOV
225
                // If this peer's access status is temporary, we'll need to
×
UNCOV
226
                // update the peerChanInfo map. The peer's access status will
×
UNCOV
227
                // stay temporary.
×
UNCOV
228
                peerCount, found := a.peerChanInfo[peerMapKey]
×
UNCOV
229
                if !found {
×
230
                        // Error if we did not find any info in peerChanInfo.
×
231
                        acsmLog.ErrorS(ctx, "Pending peer info not found",
×
232
                                ErrNoPendingPeerInfo)
×
233

×
234
                        return ErrNoPendingPeerInfo
×
235
                }
×
236

237
                // Increment the pending channel amount.
UNCOV
238
                peerCount.PendingOpenCount += 1
×
UNCOV
239
                a.peerChanInfo[peerMapKey] = peerCount
×
UNCOV
240

×
UNCOV
241
                acsmLog.DebugS(ctx, "Peer is temporary, incremented "+
×
UNCOV
242
                        "pending count",
×
UNCOV
243
                        "pending_count", peerCount.PendingOpenCount)
×
244

245
        case peerStatusRestricted:
1✔
246
                // If the peer's access status is restricted, then we can
1✔
247
                // transition it to a temporary-access peer. We'll need to
1✔
248
                // update numRestricted and also peerScores. We'll also need to
1✔
249
                // update peerChanInfo.
1✔
250
                peerCount := channeldb.ChanCount{
1✔
251
                        HasOpenOrClosedChan: false,
1✔
252
                        PendingOpenCount:    1,
1✔
253
                }
1✔
254

1✔
255
                a.peerChanInfo[peerMapKey] = peerCount
1✔
256

1✔
257
                // A restricted-access slot has opened up.
1✔
258
                oldRestricted := a.numRestricted
1✔
259
                a.numRestricted -= 1
1✔
260

1✔
261
                a.peerScores[peerMapKey] = peerSlotStatus{
1✔
262
                        state: peerStatusTemporary,
1✔
263
                }
1✔
264

1✔
265
                acsmLog.InfoS(ctx, "Peer transitioned restricted -> "+
1✔
266
                        "temporary (pending open)",
1✔
267
                        "old_restricted", oldRestricted,
1✔
268
                        "new_restricted", a.numRestricted)
1✔
269

270
        default:
×
271
                // This should not be possible.
×
272
                err := fmt.Errorf("invalid peer access status %v for %x",
×
273
                        status.state, peerMapKey)
×
274
                acsmLog.ErrorS(ctx, "Invalid peer access status", err)
×
275

×
276
                return err
×
277
        }
278

279
        return nil
1✔
280
}
281

282
// newPendingCloseChan is called when a pending-open channel prematurely closes
283
// before the funding transaction has confirmed. This potentially demotes a
284
// temporary-access peer to a restricted-access peer. If no restricted-access
285
// slots are available, the peer will be disconnected.
286
func (a *accessMan) newPendingCloseChan(remotePub *btcec.PublicKey) error {
1✔
287
        a.banScoreMtx.Lock()
1✔
288
        defer a.banScoreMtx.Unlock()
1✔
289

1✔
290
        ctx := btclog.WithCtx(
1✔
291
                context.TODO(), lnutils.LogPubKey("peer", remotePub),
1✔
292
        )
1✔
293

1✔
294
        acsmLog.DebugS(ctx, "Processing pending channel close")
1✔
295

1✔
296
        peerMapKey := string(remotePub.SerializeCompressed())
1✔
297

1✔
298
        // Fetch the peer's access status from peerScores.
1✔
299
        status, found := a.peerScores[peerMapKey]
1✔
300
        if !found {
1✔
301
                acsmLog.ErrorS(ctx, "Peer score not found", ErrNoPeerScore)
×
302

×
303
                return ErrNoPeerScore
×
304
        }
×
305

306
        switch status.state {
1✔
307
        case peerStatusProtected:
×
308
                // If this peer is protected, we don't do anything.
×
309
                acsmLog.DebugS(ctx, "Peer is protected, no change")
×
310

×
311
                return nil
×
312

313
        case peerStatusTemporary:
1✔
314
                // If this peer is temporary, we need to check if it will
1✔
315
                // revert to a restricted-access peer.
1✔
316
                peerCount, found := a.peerChanInfo[peerMapKey]
1✔
317
                if !found {
1✔
318
                        acsmLog.ErrorS(ctx, "Pending peer info not found",
×
319
                                ErrNoPendingPeerInfo)
×
320

×
321
                        // Error if we did not find any info in peerChanInfo.
×
322
                        return ErrNoPendingPeerInfo
×
323
                }
×
324

325
                currentNumPending := peerCount.PendingOpenCount - 1
1✔
326

1✔
327
                acsmLog.DebugS(ctx, "Peer is temporary, decrementing "+
1✔
328
                        "pending count",
1✔
329
                        "pending_count", currentNumPending)
1✔
330

1✔
331
                if currentNumPending == 0 {
2✔
332
                        // Remove the entry from peerChanInfo.
1✔
333
                        delete(a.peerChanInfo, peerMapKey)
1✔
334

1✔
335
                        // If this is the only pending-open channel for this
1✔
336
                        // peer and it's getting removed, attempt to demote
1✔
337
                        // this peer to a restricted peer.
1✔
338
                        if a.numRestricted == a.cfg.maxRestrictedSlots {
2✔
339
                                // There are no available restricted slots, so
1✔
340
                                // we need to disconnect this peer. We leave
1✔
341
                                // this up to the caller.
1✔
342
                                acsmLog.WarnS(ctx, "Peer last pending "+
1✔
343
                                        "channel closed: ",
1✔
344
                                        ErrNoMoreRestrictedAccessSlots,
1✔
345
                                        "num_restricted", a.numRestricted,
1✔
346
                                        "max_restricted", a.cfg.maxRestrictedSlots)
1✔
347

1✔
348
                                return ErrNoMoreRestrictedAccessSlots
1✔
349
                        }
1✔
350

351
                        // Otherwise, there is an available restricted-access
352
                        // slot, so we can demote this peer.
UNCOV
353
                        a.peerScores[peerMapKey] = peerSlotStatus{
×
UNCOV
354
                                state: peerStatusRestricted,
×
UNCOV
355
                        }
×
UNCOV
356

×
UNCOV
357
                        // Update numRestricted.
×
UNCOV
358
                        oldRestricted := a.numRestricted
×
UNCOV
359
                        a.numRestricted++
×
UNCOV
360

×
UNCOV
361
                        acsmLog.InfoS(ctx, "Peer transitioned "+
×
UNCOV
362
                                "temporary -> restricted "+
×
UNCOV
363
                                "(last pending closed)",
×
UNCOV
364
                                "old_restricted", oldRestricted,
×
UNCOV
365
                                "new_restricted", a.numRestricted)
×
UNCOV
366

×
UNCOV
367
                        return nil
×
368
                }
369

370
                // Else, we don't need to demote this peer since it has other
371
                // pending-open channels with us.
372
                peerCount.PendingOpenCount = currentNumPending
×
373
                a.peerChanInfo[peerMapKey] = peerCount
×
374

×
375
                acsmLog.DebugS(ctx, "Peer still has other pending channels",
×
376
                        "pending_count", currentNumPending)
×
377

×
378
                return nil
×
379

380
        case peerStatusRestricted:
×
381
                // This should not be possible. This indicates an error.
×
382
                err := fmt.Errorf("invalid peer access state transition: "+
×
383
                        "pending close for restricted peer %x", peerMapKey)
×
384
                acsmLog.ErrorS(ctx, "Invalid peer access state transition", err)
×
385

×
386
                return err
×
387

388
        default:
×
389
                // This should not be possible.
×
390
                err := fmt.Errorf("invalid peer access status %v for %x",
×
391
                        status.state, peerMapKey)
×
392
                acsmLog.ErrorS(ctx, "Invalid peer access status", err)
×
393

×
394
                return err
×
395
        }
396
}
397

398
// newOpenChan is called when a pending-open channel becomes an open channel
399
// (i.e. the funding transaction has confirmed). If the remote peer is a
400
// temporary-access peer, it will be promoted to a protected-access peer.
401
func (a *accessMan) newOpenChan(remotePub *btcec.PublicKey) error {
1✔
402
        a.banScoreMtx.Lock()
1✔
403
        defer a.banScoreMtx.Unlock()
1✔
404

1✔
405
        ctx := btclog.WithCtx(
1✔
406
                context.TODO(), lnutils.LogPubKey("peer", remotePub),
1✔
407
        )
1✔
408

1✔
409
        acsmLog.DebugS(ctx, "Processing new open channel")
1✔
410

1✔
411
        peerMapKey := string(remotePub.SerializeCompressed())
1✔
412

1✔
413
        // Fetch the peer's access status from peerScores.
1✔
414
        status, found := a.peerScores[peerMapKey]
1✔
415
        if !found {
1✔
UNCOV
416
                // If we didn't find the peer, we'll return an error.
×
UNCOV
417
                acsmLog.ErrorS(ctx, "Peer score not found", ErrNoPeerScore)
×
UNCOV
418

×
UNCOV
419
                return ErrNoPeerScore
×
UNCOV
420
        }
×
421

422
        switch status.state {
1✔
UNCOV
423
        case peerStatusProtected:
×
UNCOV
424
                acsmLog.DebugS(ctx, "Peer already protected, no change")
×
UNCOV
425

×
UNCOV
426
                // If the peer's state is already protected, we don't need to do
×
UNCOV
427
                // anything more.
×
UNCOV
428
                return nil
×
429

430
        case peerStatusTemporary:
1✔
431
                // If the peer's state is temporary, we'll upgrade the peer to
1✔
432
                // a protected peer.
1✔
433
                peerCount, found := a.peerChanInfo[peerMapKey]
1✔
434
                if !found {
1✔
435
                        // Error if we did not find any info in peerChanInfo.
×
436
                        acsmLog.ErrorS(ctx, "Pending peer info not found",
×
437
                                ErrNoPendingPeerInfo)
×
438

×
439
                        return ErrNoPendingPeerInfo
×
440
                }
×
441

442
                peerCount.HasOpenOrClosedChan = true
1✔
443
                peerCount.PendingOpenCount -= 1
1✔
444

1✔
445
                a.peerChanInfo[peerMapKey] = peerCount
1✔
446

1✔
447
                newStatus := peerSlotStatus{
1✔
448
                        state: peerStatusProtected,
1✔
449
                }
1✔
450
                a.peerScores[peerMapKey] = newStatus
1✔
451

1✔
452
                acsmLog.InfoS(ctx, "Peer transitioned temporary -> "+
1✔
453
                        "protected (channel opened)")
1✔
454

1✔
455
                return nil
1✔
456

457
        case peerStatusRestricted:
×
458
                // This should not be possible. For the server to receive a
×
459
                // state-transition event via NewOpenChan, the server must have
×
460
                // previously granted this peer "temporary" access. This
×
461
                // temporary access would not have been revoked or downgraded
×
462
                // without `CloseChannel` being called with the pending
×
463
                // argument set to true. This means that an open-channel state
×
464
                // transition would be impossible. Therefore, we can return an
×
465
                // error.
×
466
                err := fmt.Errorf("invalid peer access status: new open "+
×
467
                        "channel for restricted peer %x", peerMapKey)
×
468

×
469
                acsmLog.ErrorS(ctx, "Invalid peer access status", err)
×
470

×
471
                return err
×
472

473
        default:
×
474
                // This should not be possible.
×
475
                err := fmt.Errorf("invalid peer access status %v for %x",
×
476
                        status.state, peerMapKey)
×
477

×
478
                acsmLog.ErrorS(ctx, "Invalid peer access status", err)
×
479

×
480
                return err
×
481
        }
482
}
483

484
// checkAcceptIncomingConn checks whether, given the remote's public hex-
485
// encoded key, we should not accept this incoming connection or immediately
486
// disconnect. This does not assign to the server's peerScores maps. This is
487
// just an inbound filter that the brontide listeners use.
488
//
489
// TODO(yy): We should also consider removing this `checkAcceptIncomingConn`
490
// check as a) it doesn't check for ban score; and b) we should, and already
491
// have this check when we handle incoming connection in `InboundPeerConnected`.
492
func (a *accessMan) checkAcceptIncomingConn(remotePub *btcec.PublicKey) (
493
        bool, error) {
5✔
494

5✔
495
        ctx := btclog.WithCtx(
5✔
496
                context.TODO(), lnutils.LogPubKey("peer", remotePub),
5✔
497
        )
5✔
498

5✔
499
        peerMapKey := string(remotePub.SerializeCompressed())
5✔
500

5✔
501
        acsmLog.TraceS(ctx, "Checking incoming connection ban score")
5✔
502

5✔
503
        a.banScoreMtx.RLock()
5✔
504
        defer a.banScoreMtx.RUnlock()
5✔
505

5✔
506
        _, found := a.peerChanInfo[peerMapKey]
5✔
507

5✔
508
        // Exit early if found.
5✔
509
        if found {
8✔
510
                acsmLog.DebugS(ctx, "Peer found (protected/temporary), "+
3✔
511
                        "accepting")
3✔
512

3✔
513
                return true, nil
3✔
514
        }
3✔
515

516
        _, found = a.peerScores[peerMapKey]
2✔
517

2✔
518
        // Exit early if found.
2✔
519
        if found {
2✔
UNCOV
520
                acsmLog.DebugS(ctx, "Found existing peer, accepting")
×
UNCOV
521

×
UNCOV
522
                return true, nil
×
UNCOV
523
        }
×
524

525
        acsmLog.DebugS(ctx, "Peer not found in counts, checking restricted "+
2✔
526
                "slots")
2✔
527

2✔
528
        // Check numRestricted to see if there is an available slot. In
2✔
529
        // the future, it's possible to add better heuristics.
2✔
530
        if a.numRestricted < a.cfg.maxRestrictedSlots {
4✔
531
                // There is an available slot.
2✔
532
                acsmLog.DebugS(ctx, "Restricted slot available, accepting ",
2✔
533
                        "num_restricted", a.numRestricted, "max_restricted",
2✔
534
                        a.cfg.maxRestrictedSlots)
2✔
535

2✔
536
                return true, nil
2✔
537
        }
2✔
538

539
        // If there are no slots left, then we reject this connection.
UNCOV
540
        acsmLog.WarnS(ctx, "No restricted slots available, rejecting ",
×
UNCOV
541
                ErrNoMoreRestrictedAccessSlots, "num_restricted",
×
UNCOV
542
                a.numRestricted, "max_restricted", a.cfg.maxRestrictedSlots)
×
UNCOV
543

×
UNCOV
544
        return false, ErrNoMoreRestrictedAccessSlots
×
545
}
546

547
// addPeerAccess tracks a peer's access in the maps. This should be called when
548
// the peer has fully connected.
549
func (a *accessMan) addPeerAccess(remotePub *btcec.PublicKey,
550
        access peerAccessStatus, inbound bool) {
11✔
551

11✔
552
        ctx := btclog.WithCtx(
11✔
553
                context.TODO(), lnutils.LogPubKey("peer", remotePub),
11✔
554
        )
11✔
555

11✔
556
        acsmLog.DebugS(ctx, "Adding peer access", "access", access)
11✔
557

11✔
558
        // Add the remote public key to peerScores.
11✔
559
        a.banScoreMtx.Lock()
11✔
560
        defer a.banScoreMtx.Unlock()
11✔
561

11✔
562
        peerMapKey := string(remotePub.SerializeCompressed())
11✔
563

11✔
564
        // Exit early if this is an existing peer, which means it won't take
11✔
565
        // another slot.
11✔
566
        _, found := a.peerScores[peerMapKey]
11✔
567
        if found {
13✔
568
                acsmLog.DebugS(ctx, "Skipped taking restricted slot for "+
2✔
569
                        "existing peer")
2✔
570

2✔
571
                return
2✔
572
        }
2✔
573

574
        a.peerScores[peerMapKey] = peerSlotStatus{state: access}
9✔
575

9✔
576
        // Exit early if this is not a restricted peer.
9✔
577
        if access != peerStatusRestricted {
14✔
578
                acsmLog.DebugS(ctx, "Skipped taking restricted slot as peer "+
5✔
579
                        "already has access", "access", access)
5✔
580

5✔
581
                return
5✔
582
        }
5✔
583

584
        // Increment numRestricted if this is an inbound connection.
585
        if inbound {
7✔
586
                oldRestricted := a.numRestricted
3✔
587
                a.numRestricted++
3✔
588

3✔
589
                acsmLog.DebugS(ctx, "Incremented restricted slots",
3✔
590
                        "old_restricted", oldRestricted,
3✔
591
                        "new_restricted", a.numRestricted)
3✔
592

3✔
593
                return
3✔
594
        }
3✔
595

596
        // Otherwise, this is a newly created outbound connection. We won't
597
        // place any restriction on it, instead, we will do a hot upgrade here
598
        // to move it from restricted to temporary.
599
        peerCount := channeldb.ChanCount{
1✔
600
                HasOpenOrClosedChan: false,
1✔
601
                PendingOpenCount:    0,
1✔
602
        }
1✔
603

1✔
604
        a.peerChanInfo[peerMapKey] = peerCount
1✔
605
        a.peerScores[peerMapKey] = peerSlotStatus{
1✔
606
                state: peerStatusTemporary,
1✔
607
        }
1✔
608

1✔
609
        acsmLog.InfoS(ctx, "Upgraded outbound peer: restricted -> temporary")
1✔
610
}
611

612
// removePeerAccess removes the peer's access from the maps. This should be
613
// called when the peer has been disconnected.
614
func (a *accessMan) removePeerAccess(ctx context.Context, peerPubKey string) {
5✔
615
        acsmLog.DebugS(ctx, "Removing access:")
5✔
616

5✔
617
        a.banScoreMtx.Lock()
5✔
618
        defer a.banScoreMtx.Unlock()
5✔
619

5✔
620
        status, found := a.peerScores[peerPubKey]
5✔
621
        if !found {
6✔
622
                acsmLog.InfoS(ctx, "Peer score not found during removal")
1✔
623
                return
1✔
624
        }
1✔
625

626
        if status.state == peerStatusRestricted {
5✔
627
                // If the status is restricted, then we decrement from
1✔
628
                // numRestrictedSlots.
1✔
629
                oldRestricted := a.numRestricted
1✔
630
                a.numRestricted--
1✔
631

1✔
632
                acsmLog.DebugS(ctx, "Decremented restricted slots",
1✔
633
                        "old_restricted", oldRestricted,
1✔
634
                        "new_restricted", a.numRestricted)
1✔
635
        }
1✔
636

637
        acsmLog.TraceS(ctx, "Deleting from peerScores:")
4✔
638

4✔
639
        delete(a.peerScores, peerPubKey)
4✔
640

4✔
641
        // We now check whether this peer has channels with us or not.
4✔
642
        info, found := a.peerChanInfo[peerPubKey]
4✔
643
        if !found {
5✔
644
                acsmLog.DebugS(ctx, "Chan info not found during removal:")
1✔
645
                return
1✔
646
        }
1✔
647

648
        // Exit early if the peer has channel(s) with us.
649
        if info.HasOpenOrClosedChan {
4✔
650
                acsmLog.DebugS(ctx, "Skipped removing peer with channels:")
1✔
651
                return
1✔
652
        }
1✔
653

654
        // Skip removing the peer if it has pending open/close with us.
655
        if info.PendingOpenCount != 0 {
3✔
656
                acsmLog.DebugS(ctx, "Skipped removing peer with pending "+
1✔
657
                        "channels:")
1✔
658
                return
1✔
659
        }
1✔
660

661
        // Given this peer has no channels with us, we can now remove it.
662
        delete(a.peerChanInfo, peerPubKey)
1✔
663
        acsmLog.TraceS(ctx, "Removed peer from peerChanInfo:")
1✔
664
}
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