Skip to content

Commit 0c2f045

Browse files
authored
Merge pull request #10102 from yyforyongyu/fix-UpdatesInHorizon
Catch bad gossip peer and fix `UpdatesInHorizon`
2 parents 8f489d0 + 7c46ba1 commit 0c2f045

File tree

12 files changed

+189
-75
lines changed

12 files changed

+189
-75
lines changed

config.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -720,6 +720,7 @@ func DefaultConfig() Config {
720720
MsgRateBytes: discovery.DefaultMsgBytesPerSecond,
721721
MsgBurstBytes: discovery.DefaultMsgBytesBurst,
722722
FilterConcurrency: discovery.DefaultFilterConcurrency,
723+
BanThreshold: discovery.DefaultBanThreshold,
723724
},
724725
Invoices: &lncfg.Invoices{
725726
HoldExpiryDelta: lncfg.DefaultHoldInvoiceExpiryDelta,

discovery/ban.go

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package discovery
22

33
import (
44
"errors"
5+
"math"
56
"sync"
67
"time"
78

@@ -13,15 +14,14 @@ import (
1314
)
1415

1516
const (
17+
// DefaultBanThreshold is the default value to be used for banThreshold.
18+
DefaultBanThreshold = 100
19+
1620
// maxBannedPeers limits the maximum number of banned pubkeys that
1721
// we'll store.
1822
// TODO(eugene): tune.
1923
maxBannedPeers = 10_000
2024

21-
// banThreshold is the point at which non-channel peers will be banned.
22-
// TODO(eugene): tune.
23-
banThreshold = 100
24-
2525
// banTime is the amount of time that the non-channel peer will be
2626
// banned for. Channel announcements from channel peers will be dropped
2727
// if it's not one of our channels.
@@ -126,7 +126,7 @@ func (c *cachedBanInfo) Size() (uint64, error) {
126126
}
127127

128128
// isBanned returns true if the ban score is greater than the ban threshold.
129-
func (c *cachedBanInfo) isBanned() bool {
129+
func (c *cachedBanInfo) isBanned(banThreshold uint64) bool {
130130
return c.score >= banThreshold
131131
}
132132

@@ -144,15 +144,26 @@ type banman struct {
144144

145145
wg sync.WaitGroup
146146
quit chan struct{}
147+
148+
// banThreshold is the point at which non-channel peers will be banned.
149+
banThreshold uint64
147150
}
148151

149152
// newBanman creates a new banman with the default maxBannedPeers.
150-
func newBanman() *banman {
153+
func newBanman(banThreshold uint64) *banman {
154+
// If the ban threshold is set to 0, we'll use the max value to
155+
// effectively disable banning.
156+
if banThreshold == 0 {
157+
log.Warn("Banning is disabled due to zero banThreshold")
158+
banThreshold = math.MaxUint64
159+
}
160+
151161
return &banman{
152162
peerBanIndex: lru.NewCache[[33]byte, *cachedBanInfo](
153163
maxBannedPeers,
154164
),
155-
quit: make(chan struct{}),
165+
quit: make(chan struct{}),
166+
banThreshold: banThreshold,
156167
}
157168
}
158169

@@ -193,7 +204,7 @@ func (b *banman) purgeBanEntries() {
193204
keysToRemove := make([][33]byte, 0)
194205

195206
sweepEntries := func(pubkey [33]byte, banInfo *cachedBanInfo) bool {
196-
if banInfo.isBanned() {
207+
if banInfo.isBanned(b.banThreshold) {
197208
// If the peer is banned, check if the ban timer has
198209
// expired.
199210
if banInfo.lastUpdate.Add(banTime).Before(time.Now()) {
@@ -227,7 +238,7 @@ func (b *banman) isBanned(pubkey [33]byte) bool {
227238
return false
228239

229240
default:
230-
return banInfo.isBanned()
241+
return banInfo.isBanned(b.banThreshold)
231242
}
232243
}
233244

discovery/ban_test.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,14 @@ import (
1212
func TestPurgeBanEntries(t *testing.T) {
1313
t.Parallel()
1414

15-
b := newBanman()
15+
testBanThreshold := uint64(10)
16+
17+
b := newBanman(testBanThreshold)
1618

1719
// Ban a peer by repeatedly incrementing its ban score.
1820
peer1 := [33]byte{0x00}
1921

20-
for i := 0; i < banThreshold; i++ {
22+
for range testBanThreshold {
2123
b.incrementBanScore(peer1)
2224
}
2325

discovery/chan_series.go

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,10 @@ func (c *ChanSeries) UpdatesInHorizon(chain chainhash.Hash,
120120
if err != nil {
121121
return nil, err
122122
}
123+
124+
// nodesFromChan records the nodes seen from the channels.
125+
nodesFromChan := make(map[[33]byte]struct{}, len(chansInHorizon)*2)
126+
123127
for _, channel := range chansInHorizon {
124128
// If the channel hasn't been fully advertised yet, or is a
125129
// private channel, then we'll skip it as we can't construct a
@@ -136,7 +140,21 @@ func (c *ChanSeries) UpdatesInHorizon(chain chainhash.Hash,
136140
return nil, err
137141
}
138142

139-
updates = append(updates, chanAnn)
143+
// Create a slice to hold the `channel_announcement` and
144+
// potentially two `channel_update` msgs.
145+
//
146+
// NOTE: Based on BOLT7, if a channel_announcement has no
147+
// corresponding channel_updates, we must not send the
148+
// channel_announcement. Thus we use this slice to decide we
149+
// want to send this `channel_announcement` or not. By the end
150+
// of the operation, if the len of the slice is 1, we will not
151+
// send the `channel_announcement`. Otherwise, when sending the
152+
// msgs, the `channel_announcement` must be sent prior to any
153+
// corresponding `channel_update` or `node_annoucement`, that's
154+
// why we create a slice here to maintain the order.
155+
chanUpdates := make([]lnwire.Message, 0, 3)
156+
chanUpdates = append(chanUpdates, chanAnn)
157+
140158
if edge1 != nil {
141159
// We don't want to send channel updates that don't
142160
// conform to the spec (anymore).
@@ -145,18 +163,32 @@ func (c *ChanSeries) UpdatesInHorizon(chain chainhash.Hash,
145163
log.Errorf("not sending invalid channel "+
146164
"update %v: %v", edge1, err)
147165
} else {
148-
updates = append(updates, edge1)
166+
chanUpdates = append(chanUpdates, edge1)
149167
}
150168
}
169+
151170
if edge2 != nil {
152171
err := netann.ValidateChannelUpdateFields(0, edge2)
153172
if err != nil {
154173
log.Errorf("not sending invalid channel "+
155174
"update %v: %v", edge2, err)
156175
} else {
157-
updates = append(updates, edge2)
176+
chanUpdates = append(chanUpdates, edge2)
158177
}
159178
}
179+
180+
// If there's no corresponding `channel_update` to send, skip
181+
// sending this `channel_announcement`.
182+
if len(chanUpdates) < 2 {
183+
continue
184+
}
185+
186+
// Append the all the msgs to the slice.
187+
updates = append(updates, chanUpdates...)
188+
189+
// Record the nodes seen.
190+
nodesFromChan[channel.Info.NodeKey1Bytes] = struct{}{}
191+
nodesFromChan[channel.Info.NodeKey2Bytes] = struct{}{}
160192
}
161193

162194
// Next, we'll send out all the node announcements that have an update
@@ -168,8 +200,15 @@ func (c *ChanSeries) UpdatesInHorizon(chain chainhash.Hash,
168200
if err != nil {
169201
return nil, err
170202
}
203+
171204
for _, nodeAnn := range nodeAnnsInHorizon {
172-
nodeAnn := nodeAnn
205+
// If this node has not been seen in the above channels, we can
206+
// skip sending its NodeAnnouncement.
207+
if _, seen := nodesFromChan[nodeAnn.PubKeyBytes]; !seen {
208+
log.Debugf("Skipping forwarding as node %x not found "+
209+
"in channel announcement", nodeAnn.PubKeyBytes)
210+
continue
211+
}
173212

174213
// Ensure we only forward nodes that are publicly advertised to
175214
// prevent leaking information about nodes.

0 commit comments

Comments
 (0)