multi: add a rpc endpoint to track the recovery process
This commit is contained in:
1 parent
c1ef5bb908
commit
987edc9d81
17 files changed
+1182
-613
No files matched your search
@@ -844,3 +844,83 @@ func (b *BtcWallet) IsSynced() (bool, int64, error) {
|
||||
|
||||
return true, bestTimestamp, nil
|
||||
}
|
||||
|
||||
// GetRecoveryInfo returns a boolean indicating whether the wallet is started
|
||||
// in recovery mode. It also returns a float64, ranging from 0 to 1,
|
||||
// representing the recovery progress made so far.
|
||||
//
|
||||
// This is a part of the WalletController interface.
|
||||
func (b *BtcWallet) GetRecoveryInfo() (bool, float64, error) {
|
||||
isRecoveryMode := true
|
||||
progress := float64(0)
|
||||
|
||||
// A zero value in RecoveryWindow indicates there is no trigger of
|
||||
// recovery mode.
|
||||
if b.cfg.RecoveryWindow == 0 {
|
||||
isRecoveryMode = false
|
||||
return isRecoveryMode, progress, nil
|
||||
}
|
||||
|
||||
// Query the wallet's birthday block height from db.
|
||||
var birthdayBlock waddrmgr.BlockStamp
|
||||
err := walletdb.View(b.db, func(tx walletdb.ReadTx) error {
|
||||
var err error
|
||||
addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)
|
||||
birthdayBlock, _, err = b.wallet.Manager.BirthdayBlock(addrmgrNs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
// The wallet won't start until the backend is synced, thus the birthday
|
||||
// block won't be set and this particular error will be returned. We'll
|
||||
// catch this error and return a progress of 0 instead.
|
||||
if waddrmgr.IsError(err, waddrmgr.ErrBirthdayBlockNotSet) {
|
||||
return isRecoveryMode, progress, nil
|
||||
}
|
||||
|
||||
return isRecoveryMode, progress, err
|
||||
}
|
||||
|
||||
// Grab the best chain state the wallet is currently aware of.
|
||||
syncState := b.wallet.Manager.SyncedTo()
|
||||
|
||||
// Next, query the chain backend to grab the info about the tip of the
|
||||
// main chain.
|
||||
//
|
||||
// NOTE: The actual recovery process is handled by the btcsuite/btcwallet.
|
||||
// The process purposefully doesn't update the best height. It might create
|
||||
// a small difference between the height queried here and the height used
|
||||
// in the recovery process, ie, the bestHeight used here might be greater,
|
||||
// showing the recovery being unfinished while it's actually done. However,
|
||||
// during a wallet rescan after the recovery, the wallet's synced height
|
||||
// will catch up and this won't be an issue.
|
||||
_, bestHeight, err := b.cfg.ChainSource.GetBestBlock()
|
||||
if err != nil {
|
||||
return isRecoveryMode, progress, err
|
||||
}
|
||||
|
||||
// The birthday block height might be greater than the current synced height
|
||||
// in a newly restored wallet, and might be greater than the chain tip if a
|
||||
// rollback happens. In that case, we will return zero progress here.
|
||||
if syncState.Height < birthdayBlock.Height ||
|
||||
bestHeight < birthdayBlock.Height {
|
||||
return isRecoveryMode, progress, nil
|
||||
}
|
||||
|
||||
// progress is the ratio of the [number of blocks processed] over the [total
|
||||
// number of blocks] needed in a recovery mode, ranging from 0 to 1, in
|
||||
// which,
|
||||
// - total number of blocks is the current chain's best height minus the
|
||||
// wallet's birthday height plus 1.
|
||||
// - number of blocks processed is the wallet's synced height minus its
|
||||
// birthday height plus 1.
|
||||
// - If the wallet is born very recently, the bestHeight can be equal to
|
||||
// the birthdayBlock.Height, and it will recovery instantly.
|
||||
progress = float64(syncState.Height-birthdayBlock.Height+1) /
|
||||
float64(bestHeight-birthdayBlock.Height+1)
|
||||
|
||||
return isRecoveryMode, progress, nil
|
||||
}
|
||||
@@ -285,6 +285,11 @@ type WalletController interface {
|
||||
// known to the wallet, expressed in Unix epoch time
|
||||
IsSynced() (bool, int64, error)
|
||||
|
||||
// GetRecoveryInfo returns a boolean indicating whether the wallet is
|
||||
// started in recovery mode. It also returns a float64 indicating the
|
||||
// recovery progress made so far.
|
||||
GetRecoveryInfo() (bool, float64, error)
|
||||
|
||||
// Start initializes the wallet, making any necessary connections,
|
||||
// starting up required goroutines etc.
|
||||
Start() error
|
||||
|
||||
@@ -45,6 +45,7 @@ import (
|
||||
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
|
||||
"github.com/lightningnetwork/lnd/lnwallet/chanfunding"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -364,6 +365,51 @@ func createTestWallet(tempTestDir string, miningNode *rpctest.Harness,
|
||||
return wallet, nil
|
||||
}
|
||||
|
||||
func testGetRecoveryInfo(miner *rpctest.Harness,
|
||||
alice, bob *lnwallet.LightningWallet, t *testing.T) {
|
||||
|
||||
// alice's wallet is in recovery mode
|
||||
expectedRecoveryMode := true
|
||||
expectedProgress := float64(1)
|
||||
|
||||
isRecoveryMode, progress, err := alice.GetRecoveryInfo()
|
||||
require.NoError(t, err, "unable to get alice's recovery info")
|
||||
|
||||
require.Equal(t,
|
||||
expectedRecoveryMode, isRecoveryMode, "recovery mode incorrect",
|
||||
)
|
||||
require.Equal(t, expectedProgress, progress, "progress incorrect")
|
||||
|
||||
// Generate 5 blocks and check the recovery process again.
|
||||
const numBlocksMined = 5
|
||||
_, err = miner.Node.Generate(numBlocksMined)
|
||||
require.NoError(t, err, "unable to mine blocks")
|
||||
|
||||
// Check the recovery process. Once synced, the progress should be 1.
|
||||
err = waitForWalletSync(miner, alice)
|
||||
require.NoError(t, err, "Couldn't sync Alice's wallet")
|
||||
|
||||
isRecoveryMode, progress, err = alice.GetRecoveryInfo()
|
||||
require.NoError(t, err, "unable to get alice's recovery info")
|
||||
|
||||
require.Equal(t,
|
||||
expectedRecoveryMode, isRecoveryMode, "recovery mode incorrect",
|
||||
)
|
||||
require.Equal(t, expectedProgress, progress, "progress incorrect")
|
||||
|
||||
// bob's wallet is not in recovery mode
|
||||
expectedRecoveryMode = false
|
||||
expectedProgress = float64(0)
|
||||
|
||||
isRecoveryMode, progress, err = bob.GetRecoveryInfo()
|
||||
require.NoError(t, err, "unable to get bob's recovery info")
|
||||
|
||||
require.Equal(t,
|
||||
expectedRecoveryMode, isRecoveryMode, "recovery mode incorrect",
|
||||
)
|
||||
require.Equal(t, expectedProgress, progress, "progress incorrect")
|
||||
}
|
||||
|
||||
func testDualFundingReservationWorkflow(miner *rpctest.Harness,
|
||||
alice, bob *lnwallet.LightningWallet, t *testing.T) {
|
||||
|
||||
@@ -2712,6 +2758,10 @@ var walletTests = []walletTestCase{
|
||||
name: "test sign create account",
|
||||
test: testSignOutputCreateAccount,
|
||||
},
|
||||
{
|
||||
name: "test get recovery info",
|
||||
test: testGetRecoveryInfo,
|
||||
},
|
||||
}
|
||||
|
||||
func clearWalletStates(a, b *lnwallet.LightningWallet) error {
|
||||
@@ -3177,6 +3227,8 @@ func runTests(t *testing.T, walletDriver *lnwallet.WalletDriver,
|
||||
NetParams: netParams,
|
||||
ChainSource: aliceClient,
|
||||
CoinType: keychain.CoinTypeTestnet,
|
||||
// wallet starts in recovery mode
|
||||
RecoveryWindow: 2,
|
||||
}
|
||||
aliceWalletController, err = walletDriver.New(aliceWalletConfig)
|
||||
if err != nil {
|
||||
@@ -3200,6 +3252,8 @@ func runTests(t *testing.T, walletDriver *lnwallet.WalletDriver,
|
||||
NetParams: netParams,
|
||||
ChainSource: bobClient,
|
||||
CoinType: keychain.CoinTypeTestnet,
|
||||
// wallet starts without recovery mode
|
||||
RecoveryWindow: 0,
|
||||
}
|
||||
bobWalletController, err = walletDriver.New(bobWalletConfig)
|
||||
if err != nil {
|
||||
|
||||
Reference in new issue
Block a user