Merge pull request #10507 from hieblmi/wallet-tip

rpcserver: add `wallet_synced` to `GetInfoResponse`
This commit is contained in:
ziggieXXX authored and GitHub committed 2026-02-05 17:55:02 -05:00
commit 14b90a27cf
7 files changed
+2573 -2465

No files matched your search

@@ -116,6 +116,13 @@
* routerrpc HTLC event subscribers now receive specific failure details for
invoice-level validation failures, avoiding ambiguous `UNKNOWN` results. [#10520](https://github.com/lightningnetwork/lnd/pull/10520)
* [A new `wallet_synced` field has been
added](https://github.com/lightningnetwork/lnd/pull/10507) to the `GetInfo`
RPC response. This field indicates whether the wallet is fully synced to the
best chain, providing the wallet's internal sync state independently from the
composite `synced_to_chain` field which also considers router and blockbeat
dispatcher states.
## lncli Updates
## Breaking Changes
+3
View File
@@ -842,6 +842,9 @@ func init() {
allTestCases = appendPrefixed(
"wallet", allTestCases, walletTestCases,
)
allTestCases = appendPrefixed(
"wallet sync", allTestCases, walletSyncTestCases,
)
allTestCases = appendPrefixed(
"coop close with external delivery", allTestCases,
coopCloseWithExternalTestCases,
+71
View File
@@ -0,0 +1,71 @@
package itest
import (
"time"
"github.com/lightningnetwork/lnd/lntest"
"github.com/stretchr/testify/require"
)
// walletSyncTestCases defines a set of tests for the wallet_synced field
// in GetInfoResponse.
var walletSyncTestCases = []*lntest.TestCase{
{
Name: "wallet synced",
TestFunc: runTestWalletSynced,
},
}
// runTestWalletSynced tests that the wallet_synced field in GetInfoResponse
// correctly reflects the wallet's sync state. It verifies that wallet_synced
// is false while the wallet is catching up to new blocks, and becomes true
// once fully synced.
func runTestWalletSynced(ht *lntest.HarnessTest) {
// Create a test node.
alice := ht.NewNodeWithCoins("Alice", nil)
// Verify wallet starts synced.
resp := alice.RPC.GetInfo()
require.True(ht, resp.WalletSynced)
ht.Logf("Alice wallet_synced=%v", resp.WalletSynced)
// Stop Alice to create a clear sync gap while we mine blocks.
require.NoError(ht, alice.Stop(), "failed to stop Alice")
// Mine blocks while Alice is offline.
const numBlocks = 40
ht.Miner().MineBlocks(numBlocks)
_, minerHeight := ht.Miner().GetBestBlock()
// Restart Alice without waiting for full chain sync.
require.NoError(
ht, alice.Start(ht.Context()), "failed to restart Alice",
)
// While Alice is behind the miner height, wallet_synced must be false.
deadline := time.Now().Add(lntest.DefaultTimeout)
for {
resp := alice.RPC.GetInfo()
if int32(resp.BlockHeight) >= minerHeight {
break
}
require.Falsef(ht, resp.WalletSynced,
"wallet_synced=true while behind "+
"(nodeHeight=%v, minerHeight=%v)",
resp.BlockHeight, minerHeight)
if time.Now().After(deadline) {
require.Fail(ht, "timed out waiting for "+
"node to catch up")
}
time.Sleep(50 * time.Millisecond)
}
// Final verification that wallet_synced is true.
require.Eventually(ht, func() bool {
return alice.RPC.GetInfo().WalletSynced
}, lntest.DefaultTimeout, 200*time.Millisecond,
"wallet should be synced after waiting")
}
+2470 -2458
View File
File diff suppressed because it is too large. Load diff
+4
View File
@@ -2118,6 +2118,10 @@ message GetInfoResponse {
// Indicates whether final htlc resolutions are stored on disk.
bool store_final_htlc_resolutions = 22;
// Whether the wallet is fully synced to the best chain. This indicates the
// wallet's internal sync state with the backing chain source.
bool wallet_synced = 23;
}
message GetDebugInfoRequest {
+4
View File
@@ -5556,6 +5556,10 @@
"store_final_htlc_resolutions": {
"type": "boolean",
"description": "Indicates whether final htlc resolutions are stored on disk."
},
"wallet_synced": {
"type": "boolean",
"description": "Whether the wallet is fully synced to the best chain. This indicates the\nwallet's internal sync state with the backing chain source."
}
}
},
+14 -7
View File
@@ -3450,6 +3450,7 @@ func (r *rpcServer) GetInfo(_ context.Context,
Features: features,
RequireHtlcInterceptor: r.cfg.RequireInterceptor,
StoreFinalHtlcResolutions: r.cfg.StoreFinalHtlcResolutions,
WalletSynced: syncInfo.isWalletSynced,
}, nil
}
@@ -9468,6 +9469,10 @@ type chainSyncInfo struct {
// - blockbeat dispatcher.
isSynced bool
// isWalletSynced specifies whether the wallet is synced to
// our chain view.
isWalletSynced bool
// bestHeight is the current height known to the chain backend.
bestHeight int32
@@ -9488,22 +9493,23 @@ func (r *rpcServer) getChainSyncInfo() (*chainSyncInfo, error) {
return nil, fmt.Errorf("unable to get best block info: %w", err)
}
isSynced, bestHeaderTimestamp, err := r.server.cc.Wallet.IsSynced()
isWalletSynced, 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.
// Create info to be returned.
info := &chainSyncInfo{
isSynced: isSynced,
bestHeight: bestHeight,
blockHash: *bestHash,
timestamp: bestHeaderTimestamp,
isWalletSynced: isWalletSynced,
bestHeight: bestHeight,
blockHash: *bestHash,
timestamp: bestHeaderTimestamp,
}
// Exit early if the wallet is not synced.
if !isSynced {
if !isWalletSynced {
rpcsLog.Debugf("Wallet is not synced to height %v yet",
bestHeight)
@@ -9518,6 +9524,7 @@ func (r *rpcServer) getChainSyncInfo() (*chainSyncInfo, error) {
// 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.
isSynced := isWalletSynced
if !r.cfg.Routing.AssumeChannelValid {
routerHeight := r.server.graphBuilder.SyncedHeight()
isSynced = uint32(bestHeight) == routerHeight