Skip to content

Commit 20e1b0c

Browse files
committed
gossiper: handle premature NodeAnnouncments
1 parent daf753d commit 20e1b0c

2 files changed

Lines changed: 173 additions & 26 deletions

File tree

discovery/gossiper.go

Lines changed: 119 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ const (
5656
// updates that we'll hold onto.
5757
maxPrematureUpdates = 100
5858

59+
// maxPrematureNodeAnnouncements tracks the max amount of premature node
60+
// announcements that we'll hold onto.
61+
maxPrematureNodeAnnouncements = 100
62+
5963
// maxFutureMessages tracks the max amount of future messages that
6064
// we'll hold onto.
6165
maxFutureMessages = 1000
@@ -502,6 +506,11 @@ type AuthenticatedGossiper struct {
502506
// ChannelAnnouncement for the channel is received.
503507
prematureChannelUpdates *lru.Cache[uint64, *cachedNetworkMsg]
504508

509+
// prematureNodeAnnouncements stores NodeAnnouncements for which we
510+
// don't yet know any associated channel. They'll be replayed once
511+
// one of the node's channels is added to the graph.
512+
prematureNodeAnnouncements *lru.Cache[route.Vertex, *cachedNetworkMsg]
513+
505514
// banman tracks our peer's ban status.
506515
banman *banman
507516

@@ -585,6 +594,9 @@ func New(cfg Config, selfKeyDesc *keychain.KeyDescriptor) *AuthenticatedGossiper
585594
prematureChannelUpdates: lru.NewCache[uint64, *cachedNetworkMsg]( //nolint: ll
586595
maxPrematureUpdates,
587596
),
597+
prematureNodeAnnouncements: lru.NewCache[route.Vertex, *cachedNetworkMsg]( //nolint: ll
598+
maxPrematureNodeAnnouncements,
599+
),
588600
channelMtx: multimutex.NewMutex[uint64](),
589601
recentRejects: lru.NewCache[rejectCacheKey, *cachedReject](
590602
maxRejectedUpdates,
@@ -2132,6 +2144,78 @@ func (d *AuthenticatedGossiper) isPremature(chanID lnwire.ShortChannelID,
21322144
return true
21332145
}
21342146

2147+
// cachePrematureNodeAnnouncement stores the passed node announcement for later
2148+
// processing. We do this when we haven't yet seen any channels for the node and
2149+
// therefore can't verify whether the announcement should be accepted.
2150+
func (d *AuthenticatedGossiper) cachePrematureNodeAnnouncement(
2151+
nodeID route.Vertex, nMsg *networkMsg) {
2152+
2153+
pMsg := &processedNetworkMsg{msg: nMsg}
2154+
2155+
earlyMsgs, err := d.prematureNodeAnnouncements.Get(nodeID)
2156+
switch {
2157+
case err == cache.ErrElementNotFound:
2158+
_, _ = d.prematureNodeAnnouncements.Put(
2159+
nodeID, &cachedNetworkMsg{
2160+
msgs: []*processedNetworkMsg{pMsg},
2161+
},
2162+
)
2163+
2164+
default:
2165+
msgs := earlyMsgs.msgs
2166+
msgs = append(msgs, pMsg)
2167+
_, _ = d.prematureNodeAnnouncements.Put(
2168+
nodeID, &cachedNetworkMsg{
2169+
msgs: msgs,
2170+
},
2171+
)
2172+
}
2173+
2174+
log.Debugf("Caching NodeAnnouncement for node=%x until a channel "+
2175+
"is known", nodeID)
2176+
}
2177+
2178+
// replayPrematureNodeAnnouncements re-queues any cached node announcements for
2179+
// the provided vertices so they can be processed now that a channel exists.
2180+
func (d *AuthenticatedGossiper) replayPrematureNodeAnnouncements(
2181+
vertices ...route.Vertex) {
2182+
2183+
for _, vertex := range vertices {
2184+
earlyMsgs, err := d.prematureNodeAnnouncements.Get(vertex)
2185+
if err == cache.ErrElementNotFound {
2186+
continue
2187+
}
2188+
if err != nil {
2189+
log.Warnf("Unable to load cached node announcement "+
2190+
"for %x: %v", vertex, err)
2191+
continue
2192+
}
2193+
2194+
for _, cached := range earlyMsgs.msgs {
2195+
if cached.processed {
2196+
continue
2197+
}
2198+
2199+
cached.processed = true
2200+
2201+
nMsg := cached.msg
2202+
d.wg.Add(1)
2203+
go func(vertex route.Vertex, replay *networkMsg) {
2204+
defer d.wg.Done()
2205+
2206+
log.Debugf("Replaying cached NodeAnnouncement "+
2207+
"for node=%x", vertex)
2208+
2209+
select {
2210+
case d.networkMsgs <- replay:
2211+
case <-d.quit:
2212+
replay.err <- ErrGossiperShuttingDown
2213+
}
2214+
}(vertex, nMsg)
2215+
}
2216+
}
2217+
}
2218+
21352219
// processNetworkAnnouncement processes a new network relate authenticated
21362220
// channel or node announcement or announcements proofs. If the announcement
21372221
// didn't affect the internal state due to either being out of date, invalid,
@@ -2490,6 +2574,24 @@ func (d *AuthenticatedGossiper) handleNodeAnnouncement(ctx context.Context,
24902574
// We'll quickly ask the router if it already has a newer update for
24912575
// this node so we can skip validating signatures if not required.
24922576
if d.cfg.Graph.IsStaleNode(ctx, nodeAnn.NodeID, timestamp) {
2577+
if nMsg.isRemote {
2578+
_, err := d.cfg.Graph.FetchNode(ctx, nodeAnn.NodeID)
2579+
switch {
2580+
case errors.Is(err, graphdb.ErrGraphNodeNotFound):
2581+
vertex := route.Vertex(nodeAnn.NodeID)
2582+
d.cachePrematureNodeAnnouncement(vertex, nMsg)
2583+
2584+
return nil, false
2585+
2586+
case err != nil:
2587+
log.Errorf("Unable to fetch node %x: %v",
2588+
nodeAnn.NodeID, err)
2589+
2590+
nMsg.err <- err
2591+
return nil, false
2592+
}
2593+
}
2594+
24932595
log.Debugf("Skipped processing stale node: %x", nodeAnn.NodeID)
24942596
nMsg.err <- nil
24952597
return nil, true
@@ -2499,12 +2601,19 @@ func (d *AuthenticatedGossiper) handleNodeAnnouncement(ctx context.Context,
24992601
log.Debugf("Adding node: %x got error: %v", nodeAnn.NodeID,
25002602
err)
25012603

2502-
if !graph.IsError(
2503-
err,
2504-
graph.ErrOutdated,
2505-
graph.ErrIgnored,
2506-
) {
2604+
switch {
2605+
case graph.IsError(err, graph.ErrIgnored):
2606+
if nMsg.isRemote {
2607+
vertex := route.Vertex(nodeAnn.NodeID)
2608+
d.cachePrematureNodeAnnouncement(vertex, nMsg)
25072609

2610+
return nil, false
2611+
}
2612+
2613+
case graph.IsError(err, graph.ErrOutdated):
2614+
// No need to log, we'll just return the error below.
2615+
2616+
default:
25082617
log.Error(err)
25092618
}
25102619

@@ -2914,6 +3023,11 @@ func (d *AuthenticatedGossiper) handleChanAnnouncement(ctx context.Context,
29143023
channelUpdates = append(channelUpdates, chanMsgs.msgs...)
29153024
}
29163025

3026+
d.replayPrematureNodeAnnouncements(
3027+
route.Vertex(edge.NodeKey1Bytes),
3028+
route.Vertex(edge.NodeKey2Bytes),
3029+
)
3030+
29173031
// Launch a new goroutine to handle each ChannelUpdate, this is to
29183032
// ensure we don't block here, as we can handle only one announcement
29193033
// at a time.

discovery/gossiper_test.go

Lines changed: 54 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,35 @@ func (r *mockGraphSource) AddNode(_ context.Context, node *models.Node,
116116
r.mu.Lock()
117117
defer r.mu.Unlock()
118118

119+
for i := range r.nodes {
120+
existing := &r.nodes[i]
121+
if existing.PubKeyBytes == node.PubKeyBytes {
122+
existing.LastUpdate = node.LastUpdate
123+
existing.Features = node.Features
124+
existing.Alias = node.Alias
125+
existing.Addresses = node.Addresses
126+
return nil
127+
}
128+
}
129+
130+
hasChannel := false
131+
for _, info := range r.infos {
132+
if info.NodeKey1Bytes == node.PubKeyBytes ||
133+
info.NodeKey2Bytes == node.PubKeyBytes {
134+
135+
hasChannel = true
136+
break
137+
}
138+
}
139+
140+
if !hasChannel {
141+
return graph.NewErrf(
142+
graph.ErrIgnored,
143+
"Ignoring node announcement for node not found in channel graph (%x)",
144+
node.PubKeyBytes[:],
145+
)
146+
}
147+
119148
r.nodes = append(r.nodes, *node)
120149
return nil
121150
}
@@ -3096,15 +3125,19 @@ func TestNodeAnnouncementNoChannels(t *testing.T) {
30963125
require.NoError(t, err, "unable to parse pubkey")
30973126
remotePeer := &mockPeer{remoteKey, nil, nil, atomic.Bool{}}
30983127

3099-
// Process the remote node announcement.
3100-
select {
3101-
case err = <-tCtx.gossiper.ProcessRemoteAnnouncement(
3128+
// Process the remote node announcement. Since we don't yet know any of
3129+
// the node's channels, the announcement should be cached and not
3130+
// resolved immediately.
3131+
errChan := tCtx.gossiper.ProcessRemoteAnnouncement(
31023132
ctx, batch.nodeAnn2, remotePeer,
3103-
):
3104-
case <-time.After(2 * time.Second):
3105-
t.Fatal("did not process remote announcement")
3133+
)
3134+
3135+
select {
3136+
case err = <-errChan:
3137+
require.NoError(t, err, "unable to process announcement")
3138+
t.Fatal("node announcement was processed before a channel was known")
3139+
case <-time.After(100 * time.Millisecond):
31063140
}
3107-
require.NoError(t, err, "unable to process announcement")
31083141

31093142
// Since no channels or node announcements were already in the graph,
31103143
// the node announcement should be ignored, and not forwarded.
@@ -3134,18 +3167,9 @@ func TestNodeAnnouncementNoChannels(t *testing.T) {
31343167
}
31353168
require.NoError(t, err, "unable to process announcement")
31363169

3137-
// Now process the node announcement again.
3138-
select {
3139-
case err = <-tCtx.gossiper.ProcessRemoteAnnouncement(
3140-
ctx, batch.nodeAnn2, remotePeer,
3141-
):
3142-
case <-time.After(2 * time.Second):
3143-
t.Fatal("did not process remote announcement")
3144-
}
3145-
require.NoError(t, err, "unable to process announcement")
3146-
3147-
// This time the node announcement should be forwarded. The same should
3148-
// the channel announcement and update be.
3170+
// At this point the cached node announcement should also be replayed,
3171+
// causing all three announcements (channel ann, channel update, node
3172+
// ann) to be broadcast.
31493173
for i := 0; i < 3; i++ {
31503174
select {
31513175
case <-tCtx.broadcastedMessage:
@@ -3154,8 +3178,15 @@ func TestNodeAnnouncementNoChannels(t *testing.T) {
31543178
}
31553179
}
31563180

3157-
// Processing the same node announcement again should be ignored, as it
3158-
// is stale.
3181+
select {
3182+
case err = <-errChan:
3183+
require.NoError(t, err, "cached node announcement should succeed")
3184+
case <-time.After(2 * time.Second):
3185+
t.Fatal("cached node announcement wasn't replayed")
3186+
}
3187+
3188+
// Now process the node announcement again which should be treated as
3189+
// stale.
31593190
select {
31603191
case err = <-tCtx.gossiper.ProcessRemoteAnnouncement(
31613192
ctx, batch.nodeAnn2, remotePeer,
@@ -3165,6 +3196,8 @@ func TestNodeAnnouncementNoChannels(t *testing.T) {
31653196
}
31663197
require.NoError(t, err, "unable to process announcement")
31673198

3199+
// Processing the same node announcement again should be ignored, as it
3200+
// is stale.
31683201
select {
31693202
case <-tCtx.broadcastedMessage:
31703203
t.Fatal("node announcement was broadcast")

0 commit comments

Comments
 (0)