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:
parent
e59e51f8e1
commit
177e31c0b3
6 changed files with 36 additions and 36 deletions
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue