rpcserver: check blockbeatDispatcher when deciding isSynced

This commit changes `GetInfo` to include `blockbeatDispatcher`'s current
state when deciding whether the system is synced to chain. Previously we
check the best height against the wallet and the channel graph, we
should also do this to the blockbeat dispatcher to make sure the
internal consumers are also synced to the best block.
This commit is contained in:
yyforyongyu 2025-02-11 14:55:37 +08:00
parent 89c4a8dfd7
commit 59759f861f
No known key found for this signature in database
GPG key ID: 9BCD95C4FF296868

View file

@ -3225,28 +3225,10 @@ func (r *rpcServer) GetInfo(_ context.Context,
idPub := r.server.identityECDH.PubKey().SerializeCompressed()
encodedIDPub := hex.EncodeToString(idPub)
bestHash, bestHeight, err := r.server.cc.ChainIO.GetBestBlock()
// Get the system's chain sync info.
syncInfo, err := r.getChainSyncInfo()
if err != nil {
return nil, fmt.Errorf("unable to get best block info: %w", err)
}
isSynced, bestHeaderTimestamp, err := r.server.cc.Wallet.IsSynced()
if err != nil {
return nil, fmt.Errorf("unable to sync PoV of the wallet "+
"with current best block in the main chain: %v", err)
}
// If the router does full channel validation, it has a lot of work to
// do for each block. So it might be possible that it isn't yet up to
// date with the most recent block, even if the wallet is. This can
// happen in environments with high CPU load (such as parallel itests).
// Since the `synced_to_chain` flag in the response of this call is used
// by many wallets (and also our itests) to make sure everything's up to
// date, we add the router's state to it. So the flag will only toggle
// to true once the router was also able to catch up.
if !r.cfg.Routing.AssumeChannelValid {
routerHeight := r.server.graphBuilder.SyncedHeight()
isSynced = isSynced && uint32(bestHeight) == routerHeight
return nil, err
}
network := lncfg.NormalizeNetwork(r.cfg.ActiveNetParams.Name)
@ -3297,15 +3279,15 @@ func (r *rpcServer) GetInfo(_ context.Context,
NumActiveChannels: activeChannels,
NumInactiveChannels: inactiveChannels,
NumPeers: uint32(len(serverPeers)),
BlockHeight: uint32(bestHeight),
BlockHash: bestHash.String(),
SyncedToChain: isSynced,
BlockHeight: uint32(syncInfo.bestHeight),
BlockHash: syncInfo.blockHash.String(),
SyncedToChain: syncInfo.isSynced,
Testnet: isTestNet,
Chains: activeChains,
Uris: uris,
Alias: nodeAnn.Alias.String(),
Color: nodeColor,
BestHeaderTimestamp: bestHeaderTimestamp,
BestHeaderTimestamp: syncInfo.timestamp,
Version: version,
CommitHash: build.CommitHash,
SyncedToGraph: isGraphSynced,
@ -8929,3 +8911,81 @@ func rpcInitiator(isInitiator bool) lnrpc.Initiator {
return lnrpc.Initiator_INITIATOR_REMOTE
}
// chainSyncInfo wraps info about the best block and whether the system is
// synced to that block.
type chainSyncInfo struct {
// isSynced specifies whether the whole system is considered synced.
// When true, it means the following subsystems are at the best height
// reported by the chain backend,
// - wallet.
// - channel graph.
// - blockbeat dispatcher.
isSynced bool
// bestHeight is the current height known to the chain backend.
bestHeight int32
// blockHash is the hash of the current block known to the chain
// backend.
blockHash chainhash.Hash
// timestamp is the block's timestamp the wallet has synced to.
timestamp int64
}
// getChainSyncInfo queries the chain backend, the wallet, the channel router
// and the blockbeat dispatcher to determine the best block and whether the
// system is considered synced.
func (r *rpcServer) getChainSyncInfo() (*chainSyncInfo, error) {
bestHash, bestHeight, err := r.server.cc.ChainIO.GetBestBlock()
if err != nil {
return nil, fmt.Errorf("unable to get best block info: %w", err)
}
isSynced, bestHeaderTimestamp, err := r.server.cc.Wallet.IsSynced()
if err != nil {
return nil, fmt.Errorf("unable to sync PoV of the wallet "+
"with current best block in the main chain: %v", err)
}
// Create an info to be returned.
info := &chainSyncInfo{
isSynced: isSynced,
bestHeight: bestHeight,
blockHash: *bestHash,
timestamp: bestHeaderTimestamp,
}
// Exit early if the wallet is not synced.
if !isSynced {
return info, nil
}
// If the router does full channel validation, it has a lot of work to
// do for each block. So it might be possible that it isn't yet up to
// date with the most recent block, even if the wallet is. This can
// happen in environments with high CPU load (such as parallel itests).
// Since the `synced_to_chain` flag in the response of this call is used
// by many wallets (and also our itests) to make sure everything's up to
// date, we add the router's state to it. So the flag will only toggle
// to true once the router was also able to catch up.
if !r.cfg.Routing.AssumeChannelValid {
routerHeight := r.server.graphBuilder.SyncedHeight()
isSynced = uint32(bestHeight) == routerHeight
}
// Exit early if the channel graph is not synced.
if !isSynced {
return info, nil
}
// Given the wallet and the channel router are synced, we now check
// whether the blockbeat dispatcher is synced.
height := r.server.blockbeatDispatcher.CurrentHeight()
// Overwrite isSynced and return.
info.isSynced = height == bestHeight
return info, nil
}