Skip to content

Commit

Permalink
Add -check and upgrade for db consistency checks
Browse files Browse the repository at this point in the history
  • Loading branch information
tuxcanfly committed May 27, 2015
1 parent b7cef61 commit 2406368
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 3 deletions.
28 changes: 28 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import (

"github.com/btcsuite/btcutil"
"github.com/btcsuite/btcwallet/legacy/keystore"
"github.com/btcsuite/btcwallet/waddrmgr"
"github.com/btcsuite/btcwallet/walletdb"
flags "github.com/btcsuite/go-flags"
)

Expand Down Expand Up @@ -66,6 +68,7 @@ var (

type config struct {
ShowVersion bool `short:"V" long:"version" description:"Display version information and exit"`
Check bool `long:"check" description:"Check and attempt to fix databases"`
Create bool `long:"create" description:"Create the wallet if it does not exist"`
CreateTemp bool `long:"createtemp" description:"Create a temporary simulation wallet (pass=password) in the data directory indicated; must call with --datadir"`
CAFile string `long:"cafile" description:"File containing root certificates to authenticate a TLS connections with btcd"`
Expand Down Expand Up @@ -376,6 +379,31 @@ func loadConfig() (*config, []string, error) {
return nil, nil, err
}

// Check existing database for consistency and attempt fixes if possible
if cfg.Check {
netDir := networkDir(cfg.DataDir, activeNet.Params)
dbPath := filepath.Join(netDir, walletDbName)
db, err := walletdb.Open("bdb", dbPath)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return nil, nil, err
}
defer db.Close()
namespace, err := db.Namespace(waddrmgrNamespaceKey)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return nil, nil, err
}
err = waddrmgr.CheckIndexes(namespace)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return nil, nil, err
}
str := "Wallet check completed."
fmt.Fprintln(os.Stdout, str)
os.Exit(0)
}

// Exit if you try to use a simulation wallet with a standard
// data directory.
if cfg.DataDir == defaultDataDir && cfg.CreateTemp {
Expand Down
78 changes: 75 additions & 3 deletions waddrmgr/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import (

const (
// LatestMgrVersion is the most recent manager version.
LatestMgrVersion = 4
LatestMgrVersion = 5
)

var (
Expand Down Expand Up @@ -612,7 +612,7 @@ func fetchAccountName(tx walletdb.Tx, account uint32) (string, error) {

val := bucket.Get(uint32ToBytes(account))
if val == nil {
str := fmt.Sprintf("account %d not found", account)
str := fmt.Sprintf("account index id #%d not found", account)
return "", managerError(ErrAccountNotFound, str, nil)
}
offset := uint32(0)
Expand All @@ -629,7 +629,7 @@ func fetchAccountByName(tx walletdb.Tx, name string) (uint32, error) {

val := bucket.Get(stringToBytes(name))
if val == nil {
str := fmt.Sprintf("account name '%s' not found", name)
str := fmt.Sprintf("account index name '%s' not found", name)
return 0, managerError(ErrAccountNotFound, str, nil)
}

Expand Down Expand Up @@ -1732,6 +1732,15 @@ func upgradeManager(namespace walletdb.Namespace, pubPassPhrase []byte, chainPar
version = 4
}

if version < 5 {
if err := upgradeToVersion5(namespace); err != nil {
return err
}

// The manager is now at version 5.
version = 5
}

// Ensure the manager is upraded to the latest version. This check is
// to intentionally cause a failure if the manager version is updated
// without writing code to handle the upgrade.
Expand Down Expand Up @@ -1956,3 +1965,66 @@ func upgradeToVersion4(namespace walletdb.Namespace, pubPassPhrase []byte) error
}
return nil
}

func upgradeToVersion5(namespace walletdb.Namespace) error {
err := namespace.Update(func(tx walletdb.Tx) error {
// Write new manager version.
return putManagerVersion(tx, 5)
})
if err != nil {
return maybeConvertDbError(err)
}
return CheckIndexes(namespace)
}

// CheckIndexes checks for any missing entries in the account id
// and name indexes and rebuils them, if required, to make sure that the
// indexes are consistent
func CheckIndexes(namespace walletdb.Namespace) error {
return namespace.Update(func(tx walletdb.Tx) error {
bucket := tx.RootBucket().Bucket(acctBucketName)

return bucket.ForEach(func(k, v []byte) error {
account := binary.LittleEndian.Uint32(k)
if v == nil {
return nil
}

row, err := deserializeAccountRow(k, v)
if err != nil {
return err
}

switch row.acctType {
case actBIP0044:
// Fetch the name from the account row
acctInfo, err := deserializeBIP0044AccountRow(k, row)
if err != nil {
return err
}
name := acctInfo.name

// Check that the bidirectional index exists for this (id, name)
// pair and rebuild entries if required
oldName, _ := fetchAccountName(tx, account)
if oldName != name {
if err := putAccountIDIndex(tx, account, name); err != nil {
return err
}
fmt.Printf("account #%d: fixed invalid id index - "+
"'%s' -> '%s'\n", account, oldName, name)
}

oldAccount, _ := fetchAccountByName(tx, name)
if oldAccount != account {
if err := putAccountNameIndex(tx, account, name); err != nil {
return err
}
fmt.Printf("account '%s': fixed invalid name index - "+
"#%d -> #%d\n", name, oldAccount, account)
}
}
return nil
})
})
}

0 comments on commit 2406368

Please sign in to comment.