Merge pull request #9424 from yyforyongyu/fix-gossip-ann

multi: fix inconsistent state in gossip syncer
This commit is contained in:
Oliver Gugger authored and GitHub committed 2025-01-23 05:25:01 -06:00
commit 49affa2dc3
10 files changed
+99 -42

No files matched your search

+15 -7
View File
@@ -813,6 +813,8 @@ func (d *AuthenticatedGossiper) stop() {
func (d *AuthenticatedGossiper) ProcessRemoteAnnouncement(msg lnwire.Message,
peer lnpeer.Peer) chan error {
log.Debugf("Processing remote msg %T from peer=%x", msg, peer.PubKey())
errChan := make(chan error, 1)
// For messages in the known set of channel series queries, we'll
@@ -835,9 +837,13 @@ func (d *AuthenticatedGossiper) ProcessRemoteAnnouncement(msg lnwire.Message,
// If we've found the message target, then we'll dispatch the
// message directly to it.
syncer.ProcessQueryMsg(m, peer.QuitSignal())
err := syncer.ProcessQueryMsg(m, peer.QuitSignal())
if err != nil {
log.Errorf("Process query msg from peer %x got %v",
peer.PubKey(), err)
}
errChan <- nil
errChan <- err
return errChan
// If a peer is updating its current update horizon, then we'll dispatch
@@ -2382,7 +2388,8 @@ func (d *AuthenticatedGossiper) handleNodeAnnouncement(nMsg *networkMsg,
timestamp := time.Unix(int64(nodeAnn.Timestamp), 0)
log.Debugf("Processing NodeAnnouncement: peer=%v, timestamp=%v, "+
"node=%x", nMsg.peer, timestamp, nodeAnn.NodeID)
"node=%x, source=%x", nMsg.peer, timestamp, nodeAnn.NodeID,
nMsg.source.SerializeCompressed())
// We'll quickly ask the router if it already has a newer update for
// this node so we can skip validating signatures if not required.
@@ -2441,7 +2448,8 @@ func (d *AuthenticatedGossiper) handleNodeAnnouncement(nMsg *networkMsg,
// TODO(roasbeef): get rid of the above
log.Debugf("Processed NodeAnnouncement: peer=%v, timestamp=%v, "+
"node=%x", nMsg.peer, timestamp, nodeAnn.NodeID)
"node=%x, source=%x", nMsg.peer, timestamp, nodeAnn.NodeID,
nMsg.source.SerializeCompressed())
return announcements, true
}
@@ -3045,9 +3053,9 @@ func (d *AuthenticatedGossiper) handleChanUpdate(nMsg *networkMsg,
edgeToUpdate = e2
}
log.Debugf("Validating ChannelUpdate: channel=%v, from node=%x, has "+
"edge=%v", chanInfo.ChannelID, pubKey.SerializeCompressed(),
edgeToUpdate != nil)
log.Debugf("Validating ChannelUpdate: channel=%v, for node=%x, has "+
"edge policy=%v", chanInfo.ChannelID,
pubKey.SerializeCompressed(), edgeToUpdate != nil)
// Validate the channel announcement with the expected public key and
// channel capacity. In the case of an invalid channel update, we'll
+2 -2
View File
@@ -529,8 +529,8 @@ func (m *SyncManager) createGossipSyncer(peer lnpeer.Peer) *GossipSyncer {
s.setSyncState(chansSynced)
s.setSyncType(PassiveSync)
log.Debugf("Created new GossipSyncer[state=%s type=%s] for peer=%v",
s.syncState(), s.SyncType(), peer)
log.Debugf("Created new GossipSyncer[state=%s type=%s] for peer=%x",
s.syncState(), s.SyncType(), peer.PubKey())
return s
}
+4 -2
View File
@@ -28,7 +28,7 @@ func randPeer(t *testing.T, quit chan struct{}) *mockPeer {
func peerWithPubkey(pk *btcec.PublicKey, quit chan struct{}) *mockPeer {
return &mockPeer{
pk: pk,
sentMsgs: make(chan lnwire.Message),
sentMsgs: make(chan lnwire.Message, 1),
quit: quit,
}
}
@@ -483,7 +483,9 @@ func TestSyncManagerWaitUntilInitialHistoricalSync(t *testing.T) {
// transition it to chansSynced to ensure the remaining syncers
// aren't started as active.
if i == 0 {
assertSyncerStatus(t, s, syncingChans, PassiveSync)
assertSyncerStatus(
t, s, waitingQueryRangeReply, PassiveSync,
)
continue
}
+42 -26
View File
@@ -475,6 +475,39 @@ func (g *GossipSyncer) Stop() {
})
}
// handleSyncingChans handles the state syncingChans for the GossipSyncer. When
// in this state, we will send a QueryChannelRange msg to our peer and advance
// the syncer's state to waitingQueryRangeReply.
func (g *GossipSyncer) handleSyncingChans() {
// Prepare the query msg.
queryRangeMsg, err := g.genChanRangeQuery(g.genHistoricalChanRangeQuery)
if err != nil {
log.Errorf("Unable to gen chan range query: %v", err)
return
}
// Acquire a lock so the following state transition is atomic.
//
// NOTE: We must lock the following steps as it's possible we get an
// immediate response (ReplyChannelRange) after sending the query msg.
// The response is handled in ProcessQueryMsg, which requires the
// current state to be waitingQueryRangeReply.
g.Lock()
defer g.Unlock()
// Send the msg to the remote peer, which is non-blocking as
// `sendToPeer` only queues the msg in Brontide.
err = g.cfg.sendToPeer(queryRangeMsg)
if err != nil {
log.Errorf("Unable to send chan range query: %v", err)
return
}
// With the message sent successfully, we'll transition into the next
// state where we wait for their reply.
g.setSyncState(waitingQueryRangeReply)
}
// channelGraphSyncer is the main goroutine responsible for ensuring that we
// properly channel graph state with the remote peer, and also that we only
// send them messages which actually pass their defined update horizon.
@@ -495,27 +528,7 @@ func (g *GossipSyncer) channelGraphSyncer() {
// understand, as we'll as responding to any other queries by
// them.
case syncingChans:
// If we're in this state, then we'll send the remote
// peer our opening QueryChannelRange message.
queryRangeMsg, err := g.genChanRangeQuery(
g.genHistoricalChanRangeQuery,
)
if err != nil {
log.Errorf("Unable to gen chan range "+
"query: %v", err)
return
}
err = g.cfg.sendToPeer(queryRangeMsg)
if err != nil {
log.Errorf("Unable to send chan range "+
"query: %v", err)
return
}
// With the message sent successfully, we'll transition
// into the next state where we wait for their reply.
g.setSyncState(waitingQueryRangeReply)
g.handleSyncingChans()
// In this state, we've sent out our initial channel range
// query and are waiting for the final response from the remote
@@ -1342,9 +1355,9 @@ func (g *GossipSyncer) ApplyGossipFilter(filter *lnwire.GossipTimestampRange) er
return err
}
log.Infof("GossipSyncer(%x): applying new update horizon: start=%v, "+
"end=%v, backlog_size=%v", g.cfg.peerPub[:], startTime, endTime,
len(newUpdatestoSend))
log.Infof("GossipSyncer(%x): applying new remote update horizon: "+
"start=%v, end=%v, backlog_size=%v", g.cfg.peerPub[:],
startTime, endTime, len(newUpdatestoSend))
// If we don't have any to send, then we can return early.
if len(newUpdatestoSend) == 0 {
@@ -1515,12 +1528,15 @@ func (g *GossipSyncer) ProcessQueryMsg(msg lnwire.Message, peerQuit <-chan struc
// Reply messages should only be expected in states where we're waiting
// for a reply.
case *lnwire.ReplyChannelRange, *lnwire.ReplyShortChanIDsEnd:
g.Lock()
syncState := g.syncState()
g.Unlock()
if syncState != waitingQueryRangeReply &&
syncState != waitingQueryChanReply {
return fmt.Errorf("received unexpected query reply "+
"message %T", msg)
return fmt.Errorf("unexpected msg %T received in "+
"state %v", msg, syncState)
}
msgChan = g.gossipMsgs