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

lightningnetwork / lnd / 13236757158

10 Feb 2025 08:39AM UTC coverage: 57.649% (-1.2%) from 58.815%
13236757158

Pull #9493

github

ziggie1984
lncli: for some cmds we don't replace the data of the response.

For some cmds it is not very practical to replace the json output
because we might pipe it into other commands. For example when
creating the route we want to pipe it into sendtoRoute.
Pull Request #9493: For some lncli cmds we should not replace the content with other data

0 of 9 new or added lines in 2 files covered. (0.0%)

19535 existing lines in 252 files now uncovered.

103517 of 179563 relevant lines covered (57.65%)

24878.49 hits per line

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

77.78
/discovery/ban.go
1
package discovery
2

3
import (
4
        "errors"
5
        "sync"
6
        "time"
7

8
        "github.com/btcsuite/btcd/btcec/v2"
9
        "github.com/lightninglabs/neutrino/cache"
10
        "github.com/lightninglabs/neutrino/cache/lru"
11
        "github.com/lightningnetwork/lnd/channeldb"
12
        "github.com/lightningnetwork/lnd/lnwire"
13
)
14

15
const (
16
        // maxBannedPeers limits the maximum number of banned pubkeys that
17
        // we'll store.
18
        // TODO(eugene): tune.
19
        maxBannedPeers = 10_000
20

21
        // banThreshold is the point at which non-channel peers will be banned.
22
        // TODO(eugene): tune.
23
        banThreshold = 100
24

25
        // banTime is the amount of time that the non-channel peer will be
26
        // banned for. Channel announcements from channel peers will be dropped
27
        // if it's not one of our channels.
28
        // TODO(eugene): tune.
29
        banTime = time.Hour * 48
30

31
        // resetDelta is the time after a peer's last ban update that we'll
32
        // reset its ban score.
33
        // TODO(eugene): tune.
34
        resetDelta = time.Hour * 48
35

36
        // purgeInterval is how often we'll remove entries from the
37
        // peerBanIndex and allow peers to be un-banned. This interval is also
38
        // used to reset ban scores of peers that aren't banned.
39
        purgeInterval = time.Minute * 10
40
)
41

42
var ErrPeerBanned = errors.New("peer has bypassed ban threshold - banning")
43

44
// ClosedChannelTracker handles closed channels being gossiped to us.
45
type ClosedChannelTracker interface {
46
        // GraphCloser is used to mark channels as closed and to check whether
47
        // certain channels are closed.
48
        GraphCloser
49

50
        // IsChannelPeer checks whether we have a channel with a peer.
51
        IsChannelPeer(*btcec.PublicKey) (bool, error)
52
}
53

54
// GraphCloser handles tracking closed channels by their scid.
55
type GraphCloser interface {
56
        // PutClosedScid marks a channel as closed so that we won't validate
57
        // channel announcements for it again.
58
        PutClosedScid(lnwire.ShortChannelID) error
59

60
        // IsClosedScid checks if a short channel id is closed.
61
        IsClosedScid(lnwire.ShortChannelID) (bool, error)
62
}
63

64
// NodeInfoInquirier handles queries relating to specific nodes and channels
65
// they may have with us.
66
type NodeInfoInquirer interface {
67
        // FetchOpenChannels returns the set of channels that we have with the
68
        // peer identified by the passed-in public key.
69
        FetchOpenChannels(*btcec.PublicKey) ([]*channeldb.OpenChannel, error)
70
}
71

72
// ScidCloserMan helps the gossiper handle closed channels that are in the
73
// ChannelGraph.
74
type ScidCloserMan struct {
75
        graph     GraphCloser
76
        channelDB NodeInfoInquirer
77
}
78

79
// NewScidCloserMan creates a new ScidCloserMan.
80
func NewScidCloserMan(graph GraphCloser,
UNCOV
81
        channelDB NodeInfoInquirer) *ScidCloserMan {
×
UNCOV
82

×
UNCOV
83
        return &ScidCloserMan{
×
UNCOV
84
                graph:     graph,
×
UNCOV
85
                channelDB: channelDB,
×
UNCOV
86
        }
×
UNCOV
87
}
×
88

89
// PutClosedScid marks scid as closed so the gossiper can ignore this channel
90
// in the future.
91
func (s *ScidCloserMan) PutClosedScid(scid lnwire.ShortChannelID) error {
×
92
        return s.graph.PutClosedScid(scid)
×
93
}
×
94

95
// IsClosedScid checks whether scid is closed so that the gossiper can ignore
96
// it.
97
func (s *ScidCloserMan) IsClosedScid(scid lnwire.ShortChannelID) (bool,
UNCOV
98
        error) {
×
UNCOV
99

×
UNCOV
100
        return s.graph.IsClosedScid(scid)
×
UNCOV
101
}
×
102

103
// IsChannelPeer checks whether we have a channel with the peer.
104
func (s *ScidCloserMan) IsChannelPeer(peerKey *btcec.PublicKey) (bool, error) {
×
105
        chans, err := s.channelDB.FetchOpenChannels(peerKey)
×
106
        if err != nil {
×
107
                return false, err
×
108
        }
×
109

110
        return len(chans) > 0, nil
×
111
}
112

113
// A compile-time constraint to ensure ScidCloserMan implements
114
// ClosedChannelTracker.
115
var _ ClosedChannelTracker = (*ScidCloserMan)(nil)
116

117
// cachedBanInfo is used to track a peer's ban score and if it is banned.
118
type cachedBanInfo struct {
119
        score      uint64
120
        lastUpdate time.Time
121
}
122

123
// Size returns the "size" of an entry.
124
func (c *cachedBanInfo) Size() (uint64, error) {
603✔
125
        return 1, nil
603✔
126
}
603✔
127

128
// isBanned returns true if the ban score is greater than the ban threshold.
129
func (c *cachedBanInfo) isBanned() bool {
211✔
130
        return c.score >= banThreshold
211✔
131
}
211✔
132

133
// banman is responsible for banning peers that are misbehaving. The banman is
134
// in-memory and will be reset upon restart of LND. If a node's pubkey is in
135
// the peerBanIndex, it has a ban score. Ban scores start at 1 and are
136
// incremented by 1 for each instance of misbehavior. It uses an LRU cache to
137
// cut down on memory usage in case there are many banned peers and to protect
138
// against DoS.
139
type banman struct {
140
        // peerBanIndex tracks our peers' ban scores and if they are banned and
141
        // for how long. The ban score is incremented when our peer gives us
142
        // gossip messages that are invalid.
143
        peerBanIndex *lru.Cache[[33]byte, *cachedBanInfo]
144

145
        wg   sync.WaitGroup
146
        quit chan struct{}
147
}
148

149
// newBanman creates a new banman with the default maxBannedPeers.
150
func newBanman() *banman {
28✔
151
        return &banman{
28✔
152
                peerBanIndex: lru.NewCache[[33]byte, *cachedBanInfo](
28✔
153
                        maxBannedPeers,
28✔
154
                ),
28✔
155
                quit: make(chan struct{}),
28✔
156
        }
28✔
157
}
28✔
158

159
// start kicks off the banman by calling purgeExpiredBans.
160
func (b *banman) start() {
27✔
161
        b.wg.Add(1)
27✔
162
        go b.purgeExpiredBans()
27✔
163
}
27✔
164

165
// stop halts the banman.
166
func (b *banman) stop() {
27✔
167
        close(b.quit)
27✔
168
        b.wg.Wait()
27✔
169
}
27✔
170

171
// purgeOldEntries removes ban entries if their ban has expired.
172
func (b *banman) purgeExpiredBans() {
27✔
173
        defer b.wg.Done()
27✔
174

27✔
175
        purgeTicker := time.NewTicker(purgeInterval)
27✔
176
        defer purgeTicker.Stop()
27✔
177

27✔
178
        for {
54✔
179
                select {
27✔
180
                case <-purgeTicker.C:
×
181
                        b.purgeBanEntries()
×
182

183
                case <-b.quit:
27✔
184
                        return
27✔
185
                }
186
        }
187
}
188

189
// purgeBanEntries does two things:
190
// - removes peers from our ban list whose ban timer is up
191
// - removes peers whose ban scores have expired.
192
func (b *banman) purgeBanEntries() {
4✔
193
        keysToRemove := make([][33]byte, 0)
4✔
194

4✔
195
        sweepEntries := func(pubkey [33]byte, banInfo *cachedBanInfo) bool {
8✔
196
                if banInfo.isBanned() {
6✔
197
                        // If the peer is banned, check if the ban timer has
2✔
198
                        // expired.
2✔
199
                        if banInfo.lastUpdate.Add(banTime).Before(time.Now()) {
3✔
200
                                keysToRemove = append(keysToRemove, pubkey)
1✔
201
                        }
1✔
202

203
                        return true
2✔
204
                }
205

206
                if banInfo.lastUpdate.Add(resetDelta).Before(time.Now()) {
3✔
207
                        // Remove non-banned peers whose ban scores have
1✔
208
                        // expired.
1✔
209
                        keysToRemove = append(keysToRemove, pubkey)
1✔
210
                }
1✔
211

212
                return true
2✔
213
        }
214

215
        b.peerBanIndex.Range(sweepEntries)
4✔
216

4✔
217
        for _, key := range keysToRemove {
6✔
218
                b.peerBanIndex.Delete(key)
2✔
219
        }
2✔
220
}
221

222
// isBanned checks whether the peer identified by the pubkey is banned.
223
func (b *banman) isBanned(pubkey [33]byte) bool {
208✔
224
        banInfo, err := b.peerBanIndex.Get(pubkey)
208✔
225
        switch {
208✔
226
        case errors.Is(err, cache.ErrElementNotFound):
1✔
227
                return false
1✔
228

229
        default:
207✔
230
                return banInfo.isBanned()
207✔
231
        }
232
}
233

234
// incrementBanScore increments a peer's ban score.
235
func (b *banman) incrementBanScore(pubkey [33]byte) {
303✔
236
        banInfo, err := b.peerBanIndex.Get(pubkey)
303✔
237
        switch {
303✔
238
        case errors.Is(err, cache.ErrElementNotFound):
5✔
239
                cachedInfo := &cachedBanInfo{
5✔
240
                        score:      1,
5✔
241
                        lastUpdate: time.Now(),
5✔
242
                }
5✔
243
                _, _ = b.peerBanIndex.Put(pubkey, cachedInfo)
5✔
244
        default:
298✔
245
                cachedInfo := &cachedBanInfo{
298✔
246
                        score:      banInfo.score + 1,
298✔
247
                        lastUpdate: time.Now(),
298✔
248
                }
298✔
249

298✔
250
                _, _ = b.peerBanIndex.Put(pubkey, cachedInfo)
298✔
251
        }
252
}
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