Merge pull request #10530 from NishantBansal2003/gossip-race

discovery: fix race on remoteUpdateHorizon in GossipSyncer
This commit is contained in:
Olaoluwa Osuntokun authored and GitHub committed 2026-02-04 17:48:58 -08:00
commit 44c6cc5a1c
2 files changed
+81 -5

No files matched your search

+74
View File
@@ -940,7 +940,11 @@ func createTestCtx(t *testing.T, startHeight uint32, isChanPeer bool) (
return lnwire.ShortChannelID{}, fmt.Errorf("no peer alias")
}
hID := lnwire.ShortChannelID{BlockHeight: startHeight}
channelSeries := newMockChannelGraphTimeSeries(hID)
gossiper := New(Config{
ChanSeries: channelSeries,
ChainIO: chain,
ChainParams: &chaincfg.MainNetParams,
Notifier: notifier,
@@ -5297,3 +5301,73 @@ func TestGossiperShutdownWrongChainAnnouncement(t *testing.T) {
// is blocked trying to send to the error channel a second time.
require.NoError(t, tCtx.gossiper.Stop())
}
// TestGossipSyncerRace verifies that there is no race when the gossiper flushes
// a pending batch of new announcements to the network while concurrently
// processing a GossipTimestampRange message from a peer.
func TestGossipSyncerRace(t *testing.T) {
t.Parallel()
tCtx, err := createTestCtx(t, 0, false)
require.NoError(t, err)
nodePeer := &mockPeer{remoteKeyPriv1.PubKey(), nil, nil, atomic.Bool{}}
// Connect the remote peer so it can send us a GossipTimestampRange
// message.
tCtx.gossiper.InitSyncState(nodePeer)
errCh := make(chan error, 1)
go func() {
// Wait for the trickle delay to elapse before sending the
// GossipTimestampRange message.
time.Sleep(trickleDelay)
gossipTimestampRange := &lnwire.GossipTimestampRange{
ChainHash: tCtx.gossiper.syncMgr.cfg.ChainHash,
FirstTimestamp: uint32(time.Now().Unix()),
TimestampRange: 3600,
}
select {
case err := <-tCtx.gossiper.ProcessRemoteAnnouncement(
t.Context(), gossipTimestampRange, nodePeer,
):
errCh <- err
case <-time.After(2 * time.Second):
errCh <- fmt.Errorf("gossip message not processed")
}
}()
// Send a channel announcement from the remote peer, which will be
// flushed to the network after the trickle delay.
ca, err := tCtx.createRemoteChannelAnnouncement(0)
require.NoError(t, err)
select {
case err := <-tCtx.gossiper.ProcessRemoteAnnouncement(
t.Context(), ca, nodePeer,
):
require.NoError(t, err)
case <-time.After(2 * time.Second):
t.Fatal("remote announcement not processed")
}
// After the trickle delay, the channel announcement is flushed to the
// network. At the same time, the peer sends a GossipTimestampRange
// message, which could trigger a race.
select {
case <-tCtx.broadcastedMessage:
case <-time.After(2 * trickleDelay):
t.Fatal("announcement was not broadcast")
}
// Ensure the goroutine completed successfully.
select {
case err := <-errCh:
require.NoError(t, err)
case <-time.After(2 * time.Second):
t.Fatal("timeout waiting for gossip message processing")
}
}
+7 -5
View File
@@ -1576,9 +1576,13 @@ func (g *GossipSyncer) ApplyGossipFilter(ctx context.Context,
func (g *GossipSyncer) FilterGossipMsgs(ctx context.Context,
msgs ...msgWithSenders) {
g.Lock()
filter := g.remoteUpdateHorizon
g.Unlock()
// If the peer doesn't have an update horizon set, then we won't send
// it any new update messages.
if g.remoteUpdateHorizon == nil {
if filter == nil {
log.Tracef("GossipSyncer(%x): skipped due to nil "+
"remoteUpdateHorizon", g.cfg.peerPub[:])
return
@@ -1619,12 +1623,10 @@ func (g *GossipSyncer) FilterGossipMsgs(ctx context.Context,
// We'll construct a helper function that we'll us below to determine
// if a given messages passes the gossip msg filter.
g.Lock()
startTime := time.Unix(int64(g.remoteUpdateHorizon.FirstTimestamp), 0)
startTime := time.Unix(int64(filter.FirstTimestamp), 0)
endTime := startTime.Add(
time.Duration(g.remoteUpdateHorizon.TimestampRange) * time.Second,
time.Duration(filter.TimestampRange) * time.Second,
)
g.Unlock()
passesFilter := func(timeStamp uint32) bool {
t := time.Unix(int64(timeStamp), 0)