Merge pull request #10530 from NishantBansal2003/gossip-race
discovery: fix race on remoteUpdateHorizon in GossipSyncer
This commit is contained in:
2 files changed
+81
-5
No files matched your search
@@ -940,7 +940,11 @@ func createTestCtx(t *testing.T, startHeight uint32, isChanPeer bool) (
|
|||||||
return lnwire.ShortChannelID{}, fmt.Errorf("no peer alias")
|
return lnwire.ShortChannelID{}, fmt.Errorf("no peer alias")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hID := lnwire.ShortChannelID{BlockHeight: startHeight}
|
||||||
|
channelSeries := newMockChannelGraphTimeSeries(hID)
|
||||||
|
|
||||||
gossiper := New(Config{
|
gossiper := New(Config{
|
||||||
|
ChanSeries: channelSeries,
|
||||||
ChainIO: chain,
|
ChainIO: chain,
|
||||||
ChainParams: &chaincfg.MainNetParams,
|
ChainParams: &chaincfg.MainNetParams,
|
||||||
Notifier: notifier,
|
Notifier: notifier,
|
||||||
@@ -5297,3 +5301,73 @@ func TestGossiperShutdownWrongChainAnnouncement(t *testing.T) {
|
|||||||
// is blocked trying to send to the error channel a second time.
|
// is blocked trying to send to the error channel a second time.
|
||||||
require.NoError(t, tCtx.gossiper.Stop())
|
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
@@ -1576,9 +1576,13 @@ func (g *GossipSyncer) ApplyGossipFilter(ctx context.Context,
|
|||||||
func (g *GossipSyncer) FilterGossipMsgs(ctx context.Context,
|
func (g *GossipSyncer) FilterGossipMsgs(ctx context.Context,
|
||||||
msgs ...msgWithSenders) {
|
msgs ...msgWithSenders) {
|
||||||
|
|
||||||
|
g.Lock()
|
||||||
|
filter := g.remoteUpdateHorizon
|
||||||
|
g.Unlock()
|
||||||
|
|
||||||
// If the peer doesn't have an update horizon set, then we won't send
|
// If the peer doesn't have an update horizon set, then we won't send
|
||||||
// it any new update messages.
|
// it any new update messages.
|
||||||
if g.remoteUpdateHorizon == nil {
|
if filter == nil {
|
||||||
log.Tracef("GossipSyncer(%x): skipped due to nil "+
|
log.Tracef("GossipSyncer(%x): skipped due to nil "+
|
||||||
"remoteUpdateHorizon", g.cfg.peerPub[:])
|
"remoteUpdateHorizon", g.cfg.peerPub[:])
|
||||||
return
|
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
|
// We'll construct a helper function that we'll us below to determine
|
||||||
// if a given messages passes the gossip msg filter.
|
// if a given messages passes the gossip msg filter.
|
||||||
g.Lock()
|
startTime := time.Unix(int64(filter.FirstTimestamp), 0)
|
||||||
startTime := time.Unix(int64(g.remoteUpdateHorizon.FirstTimestamp), 0)
|
|
||||||
endTime := startTime.Add(
|
endTime := startTime.Add(
|
||||||
time.Duration(g.remoteUpdateHorizon.TimestampRange) * time.Second,
|
time.Duration(filter.TimestampRange) * time.Second,
|
||||||
)
|
)
|
||||||
g.Unlock()
|
|
||||||
|
|
||||||
passesFilter := func(timeStamp uint32) bool {
|
passesFilter := func(timeStamp uint32) bool {
|
||||||
t := time.Unix(int64(timeStamp), 0)
|
t := time.Unix(int64(timeStamp), 0)
|
||||||
|
|||||||
Reference in new issue
Block a user