wallet: break recovery() to recovery() and rescanblockchain()
Now the recovery, which runs at startup, only scans for known addresses that were generated and recorded by this wallet. The coming rescanblockchain RPC implementation, which requires the wallet to be unlocked, does account discovery.
This commit is contained in:
parent
e03ce4c6d5
commit
fe6f28d469
1 changed files with 27 additions and 44 deletions
|
@ -26,7 +26,6 @@ import (
|
||||||
btcutil "github.com/lbryio/lbcutil"
|
btcutil "github.com/lbryio/lbcutil"
|
||||||
"github.com/lbryio/lbcutil/hdkeychain"
|
"github.com/lbryio/lbcutil/hdkeychain"
|
||||||
"github.com/lbryio/lbcwallet/chain"
|
"github.com/lbryio/lbcwallet/chain"
|
||||||
"github.com/lbryio/lbcwallet/internal/prompt"
|
|
||||||
"github.com/lbryio/lbcwallet/waddrmgr"
|
"github.com/lbryio/lbcwallet/waddrmgr"
|
||||||
"github.com/lbryio/lbcwallet/wallet/txauthor"
|
"github.com/lbryio/lbcwallet/wallet/txauthor"
|
||||||
"github.com/lbryio/lbcwallet/wallet/txrules"
|
"github.com/lbryio/lbcwallet/wallet/txrules"
|
||||||
|
@ -407,7 +406,7 @@ func (w *Wallet) syncWithChain(birthdayStamp *waddrmgr.BlockStamp) error {
|
||||||
// If the wallet requested an on-chain recovery of its funds, we'll do
|
// If the wallet requested an on-chain recovery of its funds, we'll do
|
||||||
// so now.
|
// so now.
|
||||||
if w.recoveryWindow > 0 {
|
if w.recoveryWindow > 0 {
|
||||||
if err := w.recovery(chainClient, birthdayStamp); err != nil {
|
if err := w.Recovery(chainClient); err != nil {
|
||||||
return fmt.Errorf("unable to perform wallet recovery: "+
|
return fmt.Errorf("unable to perform wallet recovery: "+
|
||||||
"%v", err)
|
"%v", err)
|
||||||
}
|
}
|
||||||
|
@ -638,13 +637,12 @@ func locateBirthdayBlock(chainClient chainConn,
|
||||||
return birthdayBlock, nil
|
return birthdayBlock, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// recovery attempts to recover any unspent outputs that pay to any of our
|
// Recovery attempts to recover any unspent outputs that pay to any of our
|
||||||
// addresses starting from our birthday, or the wallet's tip (if higher), which
|
// addresses starting from our birthday, or the wallet's tip (if higher), which
|
||||||
// would indicate resuming a recovery after a restart.
|
// would indicate resuming a recovery after a restart.
|
||||||
func (w *Wallet) recovery(chainClient chain.Interface,
|
func (w *Wallet) Recovery(chainClient chain.Interface) error {
|
||||||
birthdayBlock *waddrmgr.BlockStamp) error {
|
|
||||||
|
|
||||||
log.Infof("RECOVERY MODE ENABLED -- rescanning for used addresses "+
|
log.Infof("Recovery for used addresses "+
|
||||||
"with recovery_window=%d", w.recoveryWindow)
|
"with recovery_window=%d", w.recoveryWindow)
|
||||||
|
|
||||||
// We'll initialize the recovery manager with a default batch size of
|
// We'll initialize the recovery manager with a default batch size of
|
||||||
|
@ -672,44 +670,36 @@ func (w *Wallet) recovery(chainClient chain.Interface,
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch the best height from the backend to determine when we should
|
return nil
|
||||||
// stop.
|
|
||||||
_, bestHeight, err := chainClient.GetBestBlock()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now we can begin scanning the chain from the wallet's current tip to
|
func (w *Wallet) RescanBlockchain(chainClient chain.Interface,
|
||||||
// ensure we properly handle restarts. Since the recovery process itself
|
startHeight int32, stopHeight int32) (int32, int32, error) {
|
||||||
// acts as rescan, we'll also update our wallet's synced state along the
|
|
||||||
// way to reflect the blocks we process and prevent rescanning them
|
|
||||||
// later on.
|
|
||||||
//
|
|
||||||
// NOTE: We purposefully don't update our best height since we assume
|
|
||||||
// that a wallet rescan will be performed from the wallet's tip, which
|
|
||||||
// will be of bestHeight after completing the recovery process.
|
|
||||||
|
|
||||||
pass, err := prompt.Passphrase(false)
|
log.Infof("Rescanning blockchain from block %d to %d "+
|
||||||
if err != nil {
|
"with recovery_window=%d", startHeight, stopHeight,
|
||||||
return err
|
w.recoveryWindow)
|
||||||
}
|
|
||||||
|
|
||||||
err = w.Unlock(pass, nil)
|
defer log.Infof("Rescan blockchain done")
|
||||||
if err != nil {
|
|
||||||
return err
|
recoveryMgr := NewRecoveryManager(
|
||||||
|
w.recoveryWindow, recoveryBatchSize, w.chainParams,
|
||||||
|
)
|
||||||
|
|
||||||
|
scopedMgrs := make(map[waddrmgr.KeyScope]*waddrmgr.ScopedKeyManager)
|
||||||
|
for _, scopedMgr := range w.Manager.ActiveScopedKeyManagers() {
|
||||||
|
scopedMgrs[scopedMgr.Scope()] = scopedMgr
|
||||||
}
|
}
|
||||||
defer w.Lock()
|
|
||||||
|
|
||||||
var blocks []*waddrmgr.BlockStamp
|
var blocks []*waddrmgr.BlockStamp
|
||||||
startHeight := w.Manager.SyncedTo().Height + 1
|
for height := startHeight; height <= stopHeight; height++ {
|
||||||
for height := startHeight; height <= bestHeight; height++ {
|
|
||||||
hash, err := chainClient.GetBlockHash(int64(height))
|
hash, err := chainClient.GetBlockHash(int64(height))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return startHeight, stopHeight, err
|
||||||
}
|
}
|
||||||
header, err := chainClient.GetBlockHeader(hash)
|
header, err := chainClient.GetBlockHeader(hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return startHeight, stopHeight, err
|
||||||
}
|
}
|
||||||
blocks = append(blocks, &waddrmgr.BlockStamp{
|
blocks = append(blocks, &waddrmgr.BlockStamp{
|
||||||
Hash: *hash,
|
Hash: *hash,
|
||||||
|
@ -720,7 +710,7 @@ func (w *Wallet) recovery(chainClient chain.Interface,
|
||||||
// It's possible for us to run into blocks before our birthday
|
// It's possible for us to run into blocks before our birthday
|
||||||
// if our birthday is after our reorg safe height, so we'll make
|
// if our birthday is after our reorg safe height, so we'll make
|
||||||
// sure to not add those to the batch.
|
// sure to not add those to the batch.
|
||||||
if height >= birthdayBlock.Height {
|
if height >= startHeight {
|
||||||
recoveryMgr.AddToBlockBatch(
|
recoveryMgr.AddToBlockBatch(
|
||||||
hash, height, header.Timestamp,
|
hash, height, header.Timestamp,
|
||||||
)
|
)
|
||||||
|
@ -731,18 +721,12 @@ func (w *Wallet) recovery(chainClient chain.Interface,
|
||||||
// the recovery batch size, so we can proceed to commit our
|
// the recovery batch size, so we can proceed to commit our
|
||||||
// state to disk.
|
// state to disk.
|
||||||
recoveryBatch := recoveryMgr.BlockBatch()
|
recoveryBatch := recoveryMgr.BlockBatch()
|
||||||
if len(recoveryBatch) != recoveryBatchSize && height != bestHeight {
|
if len(recoveryBatch) != recoveryBatchSize && height != stopHeight {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
err = walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error {
|
err = walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error {
|
||||||
ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
|
ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
|
||||||
for _, block := range blocks {
|
|
||||||
err = w.Manager.SetSyncedTo(ns, block)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for scope, scopedMgr := range scopedMgrs {
|
for scope, scopedMgr := range scopedMgrs {
|
||||||
scopeState := recoveryMgr.State().StateForScope(scope)
|
scopeState := recoveryMgr.State().StateForScope(scope)
|
||||||
err = expandScopeHorizons(ns, scopedMgr, scopeState)
|
err = expandScopeHorizons(ns, scopedMgr, scopeState)
|
||||||
|
@ -755,7 +739,7 @@ func (w *Wallet) recovery(chainClient chain.Interface,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return startHeight, stopHeight, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(recoveryBatch) > 0 {
|
if len(recoveryBatch) > 0 {
|
||||||
|
@ -769,8 +753,7 @@ func (w *Wallet) recovery(chainClient chain.Interface,
|
||||||
blocks = blocks[:0]
|
blocks = blocks[:0]
|
||||||
recoveryMgr.ResetBlockBatch()
|
recoveryMgr.ResetBlockBatch()
|
||||||
}
|
}
|
||||||
|
return startHeight, stopHeight, nil
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// recoverScopedAddresses scans a range of blocks in attempts to recover any
|
// recoverScopedAddresses scans a range of blocks in attempts to recover any
|
||||||
|
|
Loading…
Reference in a new issue