waddrmgr+cmd/dropwtxmgr: start rescan from birthday block

In this commit, we modify the dropwtxmgr tool to force a rescan upon
restart from the wallet's birthday block, rather than the chain's
genesis block. We can safely do this as we expect that no on-chain
events relevant to the wallet should happen before this block.  For
older wallets which do not have their birthday block set, the rescan
should start from the genesis block.
This commit is contained in:
Wilmer Paulino 2019-01-08 09:44:44 -08:00 committed by Olaoluwa Osuntokun
parent e59e51f8e1
commit 177e31c0b3
6 changed files with 36 additions and 36 deletions

View file

@ -6,12 +6,12 @@ package main
import ( import (
"bufio" "bufio"
"encoding/binary"
"fmt" "fmt"
"os" "os"
"path/filepath" "path/filepath"
"github.com/btcsuite/btcutil" "github.com/btcsuite/btcutil"
"github.com/btcsuite/btcwallet/waddrmgr"
"github.com/btcsuite/btcwallet/walletdb" "github.com/btcsuite/btcwallet/walletdb"
_ "github.com/btcsuite/btcwallet/walletdb/bdb" _ "github.com/btcsuite/btcwallet/walletdb/bdb"
"github.com/btcsuite/btcwallet/wtxmgr" "github.com/btcsuite/btcwallet/wtxmgr"
@ -40,14 +40,8 @@ func init() {
var ( var (
// Namespace keys. // Namespace keys.
syncBucketName = []byte("sync")
waddrmgrNamespace = []byte("waddrmgr") waddrmgrNamespace = []byte("waddrmgr")
wtxmgrNamespace = []byte("wtxmgr") wtxmgrNamespace = []byte("wtxmgr")
// Sync related key names (sync bucket).
syncedToName = []byte("syncedto")
startBlockName = []byte("startblock")
recentBlocksName = []byte("recentblocks")
) )
func yes(s string) bool { func yes(s string) bool {
@ -111,7 +105,9 @@ func mainInt() int {
return 1 return 1
} }
defer db.Close() defer db.Close()
fmt.Println("Dropping wtxmgr namespace")
fmt.Println("Dropping btcwallet transaction history")
err = walletdb.Update(db, func(tx walletdb.ReadWriteTx) error { err = walletdb.Update(db, func(tx walletdb.ReadWriteTx) error {
err := tx.DeleteTopLevelBucket(wtxmgrNamespace) err := tx.DeleteTopLevelBucket(wtxmgrNamespace)
if err != nil && err != walletdb.ErrBucketNotFound { if err != nil && err != walletdb.ErrBucketNotFound {
@ -125,17 +121,21 @@ func mainInt() int {
if err != nil { if err != nil {
return err return err
} }
ns = tx.ReadWriteBucket(waddrmgrNamespace).NestedReadWriteBucket(syncBucketName)
startBlock := ns.Get(startBlockName) ns = tx.ReadWriteBucket(waddrmgrNamespace)
err = ns.Put(syncedToName, startBlock) birthdayBlock, err := waddrmgr.FetchBirthdayBlock(ns)
if err != nil { if err != nil {
return err fmt.Println("Wallet does not have a birthday block " +
"set, falling back to rescan from genesis")
startBlock, err := waddrmgr.FetchStartBlock(ns)
if err != nil {
return err
}
return waddrmgr.PutSyncedTo(ns, startBlock)
} }
recentBlocks := make([]byte, 40)
copy(recentBlocks[0:4], startBlock[0:4]) return waddrmgr.PutSyncedTo(ns, &birthdayBlock)
copy(recentBlocks[8:], startBlock[4:])
binary.LittleEndian.PutUint32(recentBlocks[4:8], uint32(1))
return ns.Put(recentBlocksName, recentBlocks)
}) })
if err != nil { if err != nil {
fmt.Println("Failed to drop and re-create namespace:", err) fmt.Println("Failed to drop and re-create namespace:", err)

View file

@ -1830,8 +1830,8 @@ func fetchSyncedTo(ns walletdb.ReadBucket) (*BlockStamp, error) {
return &bs, nil return &bs, nil
} }
// putSyncedTo stores the provided synced to blockstamp to the database. // PutSyncedTo stores the provided synced to blockstamp to the database.
func putSyncedTo(ns walletdb.ReadWriteBucket, bs *BlockStamp) error { func PutSyncedTo(ns walletdb.ReadWriteBucket, bs *BlockStamp) error {
bucket := ns.NestedReadWriteBucket(syncBucketName) bucket := ns.NestedReadWriteBucket(syncBucketName)
errStr := fmt.Sprintf("failed to store sync information %v", bs.Hash) errStr := fmt.Sprintf("failed to store sync information %v", bs.Hash)
@ -1893,9 +1893,9 @@ func fetchBlockHash(ns walletdb.ReadBucket, height int32) (*chainhash.Hash, erro
return &hash, nil return &hash, nil
} }
// fetchStartBlock loads the start block stamp for the manager from the // FetchStartBlock loads the start block stamp for the manager from the
// database. // database.
func fetchStartBlock(ns walletdb.ReadBucket) (*BlockStamp, error) { func FetchStartBlock(ns walletdb.ReadBucket) (*BlockStamp, error) {
bucket := ns.NestedReadBucket(syncBucketName) bucket := ns.NestedReadBucket(syncBucketName)
// The serialized start block format is: // The serialized start block format is:
@ -1964,13 +1964,13 @@ func putBirthday(ns walletdb.ReadWriteBucket, t time.Time) error {
return nil return nil
} }
// fetchBirthdayBlock retrieves the birthday block from the database. // FetchBirthdayBlock retrieves the birthday block from the database.
// //
// The block is serialized as follows: // The block is serialized as follows:
// [0:4] block height // [0:4] block height
// [4:36] block hash // [4:36] block hash
// [36:44] block timestamp // [36:44] block timestamp
func fetchBirthdayBlock(ns walletdb.ReadBucket) (BlockStamp, error) { func FetchBirthdayBlock(ns walletdb.ReadBucket) (BlockStamp, error) {
var block BlockStamp var block BlockStamp
bucket := ns.NestedReadBucket(syncBucketName) bucket := ns.NestedReadBucket(syncBucketName)

View file

@ -1405,7 +1405,7 @@ func loadManager(ns walletdb.ReadBucket, pubPassphrase []byte,
if err != nil { if err != nil {
return nil, maybeConvertDbError(err) return nil, maybeConvertDbError(err)
} }
startBlock, err := fetchStartBlock(ns) startBlock, err := FetchStartBlock(ns)
if err != nil { if err != nil {
return nil, maybeConvertDbError(err) return nil, maybeConvertDbError(err)
} }
@ -1800,7 +1800,7 @@ func Create(ns walletdb.ReadWriteBucket, seed, pubPassphrase, privPassphrase []b
} }
// Save the initial synced to state. // Save the initial synced to state.
err = putSyncedTo(ns, &syncInfo.syncedTo) err = PutSyncedTo(ns, &syncInfo.syncedTo)
if err != nil { if err != nil {
return maybeConvertDbError(err) return maybeConvertDbError(err)
} }

View file

@ -365,10 +365,10 @@ func resetSyncedBlockToBirthday(ns walletdb.ReadWriteBucket) error {
return errors.New("sync bucket does not exist") return errors.New("sync bucket does not exist")
} }
birthdayBlock, err := fetchBirthdayBlock(ns) birthdayBlock, err := FetchBirthdayBlock(ns)
if err != nil { if err != nil {
return err return err
} }
return putSyncedTo(ns, &birthdayBlock) return PutSyncedTo(ns, &birthdayBlock)
} }

View file

@ -81,7 +81,7 @@ func TestMigrationPopulateBirthdayBlock(t *testing.T) {
block.Height = i block.Height = i
blockHash := bytes.Repeat([]byte(string(i)), 32) blockHash := bytes.Repeat([]byte(string(i)), 32)
copy(block.Hash[:], blockHash) copy(block.Hash[:], blockHash)
if err := putSyncedTo(ns, block); err != nil { if err := PutSyncedTo(ns, block); err != nil {
return err return err
} }
} }
@ -100,7 +100,7 @@ func TestMigrationPopulateBirthdayBlock(t *testing.T) {
// Finally, since the migration has not yet started, we should // Finally, since the migration has not yet started, we should
// not be able to find the birthday block within the database. // not be able to find the birthday block within the database.
_, err := fetchBirthdayBlock(ns) _, err := FetchBirthdayBlock(ns)
if !IsError(err, ErrBirthdayBlockNotSet) { if !IsError(err, ErrBirthdayBlockNotSet) {
return fmt.Errorf("expected ErrBirthdayBlockNotSet, "+ return fmt.Errorf("expected ErrBirthdayBlockNotSet, "+
"got %v", err) "got %v", err)
@ -112,7 +112,7 @@ func TestMigrationPopulateBirthdayBlock(t *testing.T) {
// After the migration has completed, we should see that the birthday // After the migration has completed, we should see that the birthday
// block now exists and is set to the correct expected height. // block now exists and is set to the correct expected height.
afterMigration := func(ns walletdb.ReadWriteBucket) error { afterMigration := func(ns walletdb.ReadWriteBucket) error {
birthdayBlock, err := fetchBirthdayBlock(ns) birthdayBlock, err := FetchBirthdayBlock(ns)
if err != nil { if err != nil {
return err return err
} }
@ -151,7 +151,7 @@ func TestMigrationPopulateBirthdayBlockEstimateTooFar(t *testing.T) {
block.Height = i block.Height = i
blockHash := bytes.Repeat([]byte(string(i)), 32) blockHash := bytes.Repeat([]byte(string(i)), 32)
copy(block.Hash[:], blockHash) copy(block.Hash[:], blockHash)
if err := putSyncedTo(ns, block); err != nil { if err := PutSyncedTo(ns, block); err != nil {
return err return err
} }
} }
@ -184,7 +184,7 @@ func TestMigrationPopulateBirthdayBlockEstimateTooFar(t *testing.T) {
// Finally, since the migration has not yet started, we should // Finally, since the migration has not yet started, we should
// not be able to find the birthday block within the database. // not be able to find the birthday block within the database.
_, err := fetchBirthdayBlock(ns) _, err := FetchBirthdayBlock(ns)
if !IsError(err, ErrBirthdayBlockNotSet) { if !IsError(err, ErrBirthdayBlockNotSet) {
return fmt.Errorf("expected ErrBirthdayBlockNotSet, "+ return fmt.Errorf("expected ErrBirthdayBlockNotSet, "+
"got %v", err) "got %v", err)
@ -196,7 +196,7 @@ func TestMigrationPopulateBirthdayBlockEstimateTooFar(t *testing.T) {
// After the migration has completed, we should see that the birthday // After the migration has completed, we should see that the birthday
// block now exists and is set to the correct expected height. // block now exists and is set to the correct expected height.
afterMigration := func(ns walletdb.ReadWriteBucket) error { afterMigration := func(ns walletdb.ReadWriteBucket) error {
birthdayBlock, err := fetchBirthdayBlock(ns) birthdayBlock, err := FetchBirthdayBlock(ns)
if err != nil { if err != nil {
return err return err
} }
@ -230,7 +230,7 @@ func TestMigrationResetSyncedBlockToBirthday(t *testing.T) {
block.Height = i block.Height = i
blockHash := bytes.Repeat([]byte(string(i)), 32) blockHash := bytes.Repeat([]byte(string(i)), 32)
copy(block.Hash[:], blockHash) copy(block.Hash[:], blockHash)
if err := putSyncedTo(ns, block); err != nil { if err := PutSyncedTo(ns, block); err != nil {
return err return err
} }
} }

View file

@ -59,7 +59,7 @@ func (m *Manager) SetSyncedTo(ns walletdb.ReadWriteBucket, bs *BlockStamp) error
} }
// Update the database. // Update the database.
err := putSyncedTo(ns, bs) err := PutSyncedTo(ns, bs)
if err != nil { if err != nil {
return err return err
} }
@ -115,7 +115,7 @@ func (m *Manager) SetBirthday(ns walletdb.ReadWriteBucket,
// been used, for the manager. A boolean is also returned to indicate whether // been used, for the manager. A boolean is also returned to indicate whether
// the birthday block has been verified as correct. // the birthday block has been verified as correct.
func (m *Manager) BirthdayBlock(ns walletdb.ReadBucket) (BlockStamp, bool, error) { func (m *Manager) BirthdayBlock(ns walletdb.ReadBucket) (BlockStamp, bool, error) {
birthdayBlock, err := fetchBirthdayBlock(ns) birthdayBlock, err := FetchBirthdayBlock(ns)
if err != nil { if err != nil {
return BlockStamp{}, false, err return BlockStamp{}, false, err
} }