diff --git a/discovery/syncer.go b/discovery/syncer.go index 7281012ad..571e2d697 100644 --- a/discovery/syncer.go +++ b/discovery/syncer.go @@ -235,6 +235,12 @@ type gossipSyncerCfg struct { // replyHandler, meaning we will not reply to queries from our remote // peer. noReplyQueries bool + + // ignoreHistoricalFilters will prevent syncers from replying with + // historical data when the remote peer sets a gossip_timestamp_range. + // This prevents ranges with old start times from causing us to dump the + // graph on connect. + ignoreHistoricalFilters bool } // GossipSyncer is a struct that handles synchronizing the channel graph state @@ -951,6 +957,12 @@ func (g *GossipSyncer) ApplyGossipFilter(filter *lnwire.GossipTimestampRange) er g.Unlock() + // If requested, don't reply with historical gossip data when the remote + // peer sets their gossip timestamp range. + if g.cfg.ignoreHistoricalFilters { + return nil + } + // Now that the remote peer has applied their filter, we'll query the // database for all the messages that are beyond this filter. newUpdatestoSend, err := g.cfg.channelSeries.UpdatesInHorizon( diff --git a/discovery/syncer_test.go b/discovery/syncer_test.go index 68c4a8c92..7c1147546 100644 --- a/discovery/syncer_test.go +++ b/discovery/syncer_test.go @@ -1,8 +1,10 @@ package discovery import ( + "errors" "math" "reflect" + "sync" "testing" "time" @@ -336,6 +338,66 @@ func TestGossipSyncerFilterGossipMsgsAllInMemory(t *testing.T) { } } +// TestGossipSyncerApplyNoHistoricalGossipFilter tests that once a gossip filter +// is applied for the remote peer, then we don't send the peer all known +// messages which are within their desired time horizon. +func TestGossipSyncerApplyNoHistoricalGossipFilter(t *testing.T) { + t.Parallel() + + // First, we'll create a GossipSyncer instance with a canned sendToPeer + // message to allow us to intercept their potential sends. + _, syncer, chanSeries := newTestSyncer( + lnwire.NewShortChanIDFromInt(10), defaultEncoding, + defaultChunkSize, + ) + syncer.cfg.ignoreHistoricalFilters = true + + // We'll apply this gossip horizon for the remote peer. + remoteHorizon := &lnwire.GossipTimestampRange{ + FirstTimestamp: unixStamp(25000), + TimestampRange: uint32(1000), + } + + // After applying the gossip filter, the chan series should not be + // queried using the updated horizon. + errChan := make(chan error, 1) + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + + select { + // No query received, success. + case <-time.After(3 * time.Second): + errChan <- nil + + // Unexpected query received. + case <-chanSeries.horizonReq: + errChan <- errors.New("chan series should not have been " + + "queried") + } + }() + + // We'll now attempt to apply the gossip filter for the remote peer. + syncer.ApplyGossipFilter(remoteHorizon) + + // Ensure that the syncer's remote horizon was properly updated. + if !reflect.DeepEqual(syncer.remoteUpdateHorizon, remoteHorizon) { + t.Fatalf("expected remote horizon: %v, got: %v", + remoteHorizon, syncer.remoteUpdateHorizon) + } + + // Wait for the query check to finish. + wg.Wait() + + // Assert that no query was made as a result of applying the gossip + // filter. + err := <-errChan + if err != nil { + t.Fatalf(err.Error()) + } +} + // TestGossipSyncerApplyGossipFilter tests that once a gossip filter is applied // for the remote peer, then we send the peer all known messages which are // within their desired time horizon.