Skip to content

Commit 14b90a2

Browse files
authored
Merge pull request #10507 from hieblmi/wallet-tip
rpcserver: add `wallet_synced` to `GetInfoResponse`
2 parents 44c6cc5 + 7766c1d commit 14b90a2

File tree

7 files changed

+2570
-2462
lines changed

7 files changed

+2570
-2462
lines changed

docs/release-notes/release-notes-0.21.0.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,13 @@
116116
* routerrpc HTLC event subscribers now receive specific failure details for
117117
invoice-level validation failures, avoiding ambiguous `UNKNOWN` results. [#10520](https://github.com/lightningnetwork/lnd/pull/10520)
118118

119+
* [A new `wallet_synced` field has been
120+
added](https://github.com/lightningnetwork/lnd/pull/10507) to the `GetInfo`
121+
RPC response. This field indicates whether the wallet is fully synced to the
122+
best chain, providing the wallet's internal sync state independently from the
123+
composite `synced_to_chain` field which also considers router and blockbeat
124+
dispatcher states.
125+
119126
## lncli Updates
120127

121128
## Breaking Changes

itest/list_on_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -842,6 +842,9 @@ func init() {
842842
allTestCases = appendPrefixed(
843843
"wallet", allTestCases, walletTestCases,
844844
)
845+
allTestCases = appendPrefixed(
846+
"wallet sync", allTestCases, walletSyncTestCases,
847+
)
845848
allTestCases = appendPrefixed(
846849
"coop close with external delivery", allTestCases,
847850
coopCloseWithExternalTestCases,

itest/lnd_wallet_sync_test.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package itest
2+
3+
import (
4+
"time"
5+
6+
"github.com/lightningnetwork/lnd/lntest"
7+
"github.com/stretchr/testify/require"
8+
)
9+
10+
// walletSyncTestCases defines a set of tests for the wallet_synced field
11+
// in GetInfoResponse.
12+
var walletSyncTestCases = []*lntest.TestCase{
13+
{
14+
Name: "wallet synced",
15+
TestFunc: runTestWalletSynced,
16+
},
17+
}
18+
19+
// runTestWalletSynced tests that the wallet_synced field in GetInfoResponse
20+
// correctly reflects the wallet's sync state. It verifies that wallet_synced
21+
// is false while the wallet is catching up to new blocks, and becomes true
22+
// once fully synced.
23+
func runTestWalletSynced(ht *lntest.HarnessTest) {
24+
// Create a test node.
25+
alice := ht.NewNodeWithCoins("Alice", nil)
26+
27+
// Verify wallet starts synced.
28+
resp := alice.RPC.GetInfo()
29+
require.True(ht, resp.WalletSynced)
30+
ht.Logf("Alice wallet_synced=%v", resp.WalletSynced)
31+
32+
// Stop Alice to create a clear sync gap while we mine blocks.
33+
require.NoError(ht, alice.Stop(), "failed to stop Alice")
34+
35+
// Mine blocks while Alice is offline.
36+
const numBlocks = 40
37+
ht.Miner().MineBlocks(numBlocks)
38+
_, minerHeight := ht.Miner().GetBestBlock()
39+
40+
// Restart Alice without waiting for full chain sync.
41+
require.NoError(
42+
ht, alice.Start(ht.Context()), "failed to restart Alice",
43+
)
44+
45+
// While Alice is behind the miner height, wallet_synced must be false.
46+
deadline := time.Now().Add(lntest.DefaultTimeout)
47+
for {
48+
resp := alice.RPC.GetInfo()
49+
if int32(resp.BlockHeight) >= minerHeight {
50+
break
51+
}
52+
53+
require.Falsef(ht, resp.WalletSynced,
54+
"wallet_synced=true while behind "+
55+
"(nodeHeight=%v, minerHeight=%v)",
56+
resp.BlockHeight, minerHeight)
57+
58+
if time.Now().After(deadline) {
59+
require.Fail(ht, "timed out waiting for "+
60+
"node to catch up")
61+
}
62+
63+
time.Sleep(50 * time.Millisecond)
64+
}
65+
66+
// Final verification that wallet_synced is true.
67+
require.Eventually(ht, func() bool {
68+
return alice.RPC.GetInfo().WalletSynced
69+
}, lntest.DefaultTimeout, 200*time.Millisecond,
70+
"wallet should be synced after waiting")
71+
}

lnrpc/lightning.pb.go

Lines changed: 2467 additions & 2455 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lnrpc/lightning.proto

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2118,6 +2118,10 @@ message GetInfoResponse {
21182118

21192119
// Indicates whether final htlc resolutions are stored on disk.
21202120
bool store_final_htlc_resolutions = 22;
2121+
2122+
// Whether the wallet is fully synced to the best chain. This indicates the
2123+
// wallet's internal sync state with the backing chain source.
2124+
bool wallet_synced = 23;
21212125
}
21222126

21232127
message GetDebugInfoRequest {

lnrpc/lightning.swagger.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5556,6 +5556,10 @@
55565556
"store_final_htlc_resolutions": {
55575557
"type": "boolean",
55585558
"description": "Indicates whether final htlc resolutions are stored on disk."
5559+
},
5560+
"wallet_synced": {
5561+
"type": "boolean",
5562+
"description": "Whether the wallet is fully synced to the best chain. This indicates the\nwallet's internal sync state with the backing chain source."
55595563
}
55605564
}
55615565
},

rpcserver.go

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3450,6 +3450,7 @@ func (r *rpcServer) GetInfo(_ context.Context,
34503450
Features: features,
34513451
RequireHtlcInterceptor: r.cfg.RequireInterceptor,
34523452
StoreFinalHtlcResolutions: r.cfg.StoreFinalHtlcResolutions,
3453+
WalletSynced: syncInfo.isWalletSynced,
34533454
}, nil
34543455
}
34553456

@@ -9468,6 +9469,10 @@ type chainSyncInfo struct {
94689469
// - blockbeat dispatcher.
94699470
isSynced bool
94709471

9472+
// isWalletSynced specifies whether the wallet is synced to
9473+
// our chain view.
9474+
isWalletSynced bool
9475+
94719476
// bestHeight is the current height known to the chain backend.
94729477
bestHeight int32
94739478

@@ -9488,22 +9493,23 @@ func (r *rpcServer) getChainSyncInfo() (*chainSyncInfo, error) {
94889493
return nil, fmt.Errorf("unable to get best block info: %w", err)
94899494
}
94909495

9491-
isSynced, bestHeaderTimestamp, err := r.server.cc.Wallet.IsSynced()
9496+
isWalletSynced, bestHeaderTimestamp, err :=
9497+
r.server.cc.Wallet.IsSynced()
94929498
if err != nil {
94939499
return nil, fmt.Errorf("unable to sync PoV of the wallet "+
94949500
"with current best block in the main chain: %v", err)
94959501
}
94969502

9497-
// Create an info to be returned.
9503+
// Create info to be returned.
94989504
info := &chainSyncInfo{
9499-
isSynced: isSynced,
9500-
bestHeight: bestHeight,
9501-
blockHash: *bestHash,
9502-
timestamp: bestHeaderTimestamp,
9505+
isWalletSynced: isWalletSynced,
9506+
bestHeight: bestHeight,
9507+
blockHash: *bestHash,
9508+
timestamp: bestHeaderTimestamp,
95039509
}
95049510

95059511
// Exit early if the wallet is not synced.
9506-
if !isSynced {
9512+
if !isWalletSynced {
95079513
rpcsLog.Debugf("Wallet is not synced to height %v yet",
95089514
bestHeight)
95099515

@@ -9518,6 +9524,7 @@ func (r *rpcServer) getChainSyncInfo() (*chainSyncInfo, error) {
95189524
// by many wallets (and also our itests) to make sure everything's up to
95199525
// date, we add the router's state to it. So the flag will only toggle
95209526
// to true once the router was also able to catch up.
9527+
isSynced := isWalletSynced
95219528
if !r.cfg.Routing.AssumeChannelValid {
95229529
routerHeight := r.server.graphBuilder.SyncedHeight()
95239530
isSynced = uint32(bestHeight) == routerHeight

0 commit comments

Comments
 (0)