diff --git a/blockmanager.go b/blockmanager.go index aa9507e6..72bca506 100644 --- a/blockmanager.go +++ b/blockmanager.go @@ -18,6 +18,11 @@ import ( const ( chanBufferSize = 50 + + // blockDbNamePrefix is the prefix for the block database name. The + // database type is appended to this value to form the full block + // database name. + blockDbNamePrefix = "blocks" ) // blockMsg packages a bitcoin block message and the peer it came from together @@ -394,7 +399,13 @@ func newBlockManager(s *server) *blockManager { // loadBlockDB opens the block database and returns a handle to it. func loadBlockDB() (btcdb.Db, error) { - dbPath := filepath.Join(cfg.DataDir, activeNetParams.dbName) + // The database name is based on the database type. + dbName := blockDbNamePrefix + "_" + cfg.DbType + if cfg.DbType == "sqlite" { + dbName = dbName + ".db" + } + dbPath := filepath.Join(cfg.DataDir, dbName) + log.Infof("[BMGR] Loading block database from '%s'", dbPath) db, err := btcdb.OpenDB(cfg.DbType, dbPath) if err != nil { diff --git a/config.go b/config.go index 0de626c2..2520f6bf 100644 --- a/config.go +++ b/config.go @@ -25,11 +25,13 @@ const ( defaultMaxPeers = 8 defaultBanDuration = time.Hour * 24 defaultVerifyEnabled = false + defaultDbType = "sqlite" ) var ( defaultConfigFile = filepath.Join(btcdHomeDir(), defaultConfigFilename) - defaultDataDir = filepath.Join(btcdHomeDir(), "db") + defaultDataDir = filepath.Join(btcdHomeDir(), "data") + knownDbTypes = []string{"leveldb", "sqlite"} ) // config defines the configuration options for btcd. @@ -58,7 +60,7 @@ type config struct { UseTor bool `long:"tor" description:"Specifies the proxy server used is a Tor node"` TestNet3 bool `long:"testnet" description:"Use the test network"` RegressionTest bool `long:"regtest" description:"Use the regression test network"` - DbType string `long:"dbtype" description:"DB backend to use for Block Chain"` + DbType string `long:"dbtype" description:"Database backend to use for the Block Chain"` DebugLevel string `short:"d" long:"debuglevel" description:"Logging level {trace, debug, info, warn, error, critical}"` } @@ -99,6 +101,19 @@ func validLogLevel(logLevel string) bool { return false } +// validDbType returns whether or not dbType is a supported database type. +func validDbType(dbType string) bool { + // Would be interesting if btcdb had a 'SupportedDBs() []string' + // API to populate this field. + for _, knownType := range knownDbTypes { + if dbType == knownType { + return true + } + } + + return false +} + // normalizePeerAddress returns addr with the default peer port appended if // there is not already a port specified. func normalizePeerAddress(addr string) string { @@ -180,6 +195,7 @@ func loadConfig() (*config, []string, error) { BanDuration: defaultBanDuration, ConfigFile: defaultConfigFile, DataDir: defaultDataDir, + DbType: defaultDbType, } // Pre-parse the command line options to see if an alternative config @@ -249,13 +265,32 @@ func loadConfig() (*config, []string, error) { // Validate debug log level. if !validLogLevel(cfg.DebugLevel) { - str := "%s: The specified debug level is invalid -- parsed [%v]" + str := "%s: The specified debug level [%v] is invalid" err := errors.New(fmt.Sprintf(str, "loadConfig", cfg.DebugLevel)) fmt.Fprintln(os.Stderr, err) parser.WriteHelp(os.Stderr) return nil, nil, err } + // Validate database type. + if !validDbType(cfg.DbType) { + str := "%s: The specified database type [%v] is invalid -- " + + "supported types %v" + err := errors.New(fmt.Sprintf(str, "loadConfig", cfg.DbType, + knownDbTypes)) + fmt.Fprintln(os.Stderr, err) + parser.WriteHelp(os.Stderr) + return nil, nil, err + } + + // Append the network type to the data directory so it is "namespaced" + // per network. In addition to the block database, there are other + // pieces of data that are saved to disk such as address manager state. + // All data is specific to a network, so namespacing the data directory + // means each individual piece of serialized data does not have to + // worry about changing names per network and such. + cfg.DataDir = filepath.Join(cfg.DataDir, activeNetParams.netName) + // Don't allow ban durations that are too short. if cfg.BanDuration < time.Duration(time.Second) { str := "%s: The banduration option may not be less than 1s -- parsed [%v]" @@ -303,45 +338,7 @@ func loadConfig() (*config, []string, error) { // Add default port to all added peer addresses if needed and remove // duplicate addresses. cfg.AddPeers = normalizeAndRemoveDuplicateAddresses(cfg.AddPeers) - cfg.ConnectPeers = - normalizeAndRemoveDuplicateAddresses(cfg.ConnectPeers) + cfg.ConnectPeers = normalizeAndRemoveDuplicateAddresses(cfg.ConnectPeers) - // determine which database backend to use - // would be interesting of btcdb had a 'supportedDBs() []string' - // API to populate this field. - knownDbs := []string{"leveldb", "sqlite"} - defaultDb := "sqlite" - - if len(cfg.DbType) == 0 { - // if db was not specified, use heuristic to see what - // type the database is (if the specified database is a - // file then it is sqlite3, if it is a directory assume - // it is leveldb, if not found default to type _____ - dbPath := filepath.Join(cfg.DataDir, activeNetParams.dbName) - - fi, err := os.Stat(dbPath) - if err == nil { - if fi.IsDir() { - cfg.DbType = "leveldb" - } else { - cfg.DbType = "sqlite" - } - } else { - cfg.DbType = defaultDb - } - } else { - // does checking this here really make sense ? - typeVerified := false - for _, dbtype := range knownDbs { - if cfg.DbType == dbtype { - typeVerified = true - } - } - if typeVerified == false { - err := fmt.Errorf("Specified database type [%v] not in list of supported databases: %v", cfg.DbType, knownDbs) - fmt.Fprintln(os.Stderr, err) - return nil, nil, err - } - } return &cfg, remainingArgs, nil } diff --git a/params.go b/params.go index 4d5da2c6..e6bdc882 100644 --- a/params.go +++ b/params.go @@ -17,7 +17,7 @@ var activeNetParams = netParams(defaultBtcnet) // params is used to group parameters for various networks such as the main // network and test networks. type params struct { - dbName string + netName string btcnet btcwire.BitcoinNet genesisBlock *btcwire.MsgBlock genesisHash *btcwire.ShaHash @@ -32,7 +32,7 @@ type params struct { // mainNetParams contains parameters specific to the main network // (btcwire.MainNet). var mainNetParams = params{ - dbName: "btcd.db", + netName: "mainnet", btcnet: btcwire.MainNet, genesisBlock: btcchain.ChainParams(btcwire.MainNet).GenesisBlock, genesisHash: btcchain.ChainParams(btcwire.MainNet).GenesisHash, @@ -52,7 +52,7 @@ var mainNetParams = params{ // regressionParams contains parameters specific to the regression test network // (btcwire.TestNet). var regressionParams = params{ - dbName: "btcd_regtest.db", + netName: "regtest", btcnet: btcwire.TestNet, genesisBlock: btcchain.ChainParams(btcwire.TestNet).GenesisBlock, genesisHash: btcchain.ChainParams(btcwire.TestNet).GenesisHash, @@ -67,7 +67,7 @@ var regressionParams = params{ // testNet3Params contains parameters specific to the test network (version 3) // (btcwire.TestNet3). var testNet3Params = params{ - dbName: "btcd_testnet.db", + netName: "testnet", btcnet: btcwire.TestNet3, genesisBlock: btcchain.ChainParams(btcwire.TestNet3).GenesisBlock, genesisHash: btcchain.ChainParams(btcwire.TestNet3).GenesisHash, diff --git a/upgrade.go b/upgrade.go index ebd9dfbe..dc5479ec 100644 --- a/upgrade.go +++ b/upgrade.go @@ -4,7 +4,73 @@ package main -// doUpgrades performs upgrades to btcd as new versions require it. -func doUpgrades() error { +import ( + "os" + "path/filepath" +) + +// upgradeDBPathNet moves the database for a specific network from its +// location prior to btcd version 0.2.0 and uses heuristics to ascertain the old +// database type to rename to the new format. +func upgradeDBPathNet(oldDbPath, netName string) error { + // Prior to version 0.2.0, the database was named the same thing for + // both sqlite and leveldb. Use heuristics to figure out the type + // of the database and move it to the new path and name introduced with + // version 0.2.0 accordingly. + fi, err := os.Stat(oldDbPath) + if err == nil { + oldDbType := "sqlite" + if fi.IsDir() { + oldDbType = "leveldb" + } + + // The new database name is based on the database type and + // resides in the a directory named after the network type. + newDbRoot := filepath.Join(filepath.Dir(cfg.DataDir), netName) + newDbName := blockDbNamePrefix + "_" + oldDbType + if oldDbType == "sqlite" { + newDbName = newDbName + ".db" + } + newDbPath := filepath.Join(newDbRoot, newDbName) + + // Create the new path if needed. + err = os.MkdirAll(newDbRoot, 0700) + if err != nil { + return err + } + + // Move and rename the old database. + err := os.Rename(oldDbPath, newDbPath) + if err != nil { + return err + } + } + return nil } + +// upgradeDBPaths moves the databases from their locations prior to btcd +// version 0.2.0 to their new locations. +func upgradeDBPaths() error { + // Prior to version 0.2.0, the databases were in the "db" directory and + // their names were suffixed by "testnet" and "regtest" for their + // respective networks. Check for the old database and update it to the + // new path introduced with version 0.2.0 accodingly. + oldDbRoot := filepath.Join(btcdHomeDir(), "db") + upgradeDBPathNet(filepath.Join(oldDbRoot, "btcd.db"), "mainnet") + upgradeDBPathNet(filepath.Join(oldDbRoot, "btcd_testnet.db"), "testnet") + upgradeDBPathNet(filepath.Join(oldDbRoot, "btcd_regtest.db"), "regtest") + + // Remove the old db directory. + err := os.RemoveAll(oldDbRoot) + if err != nil { + return err + } + + return nil +} + +// doUpgrades performs upgrades to btcd as new versions require it. +func doUpgrades() error { + return upgradeDBPaths() +} diff --git a/version.go b/version.go index cb52e57b..c816d0da 100644 --- a/version.go +++ b/version.go @@ -17,7 +17,7 @@ const semanticAlphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr // versioning 2.0.0 spec (http://semver.org/). const ( appMajor uint = 0 - appMinor uint = 1 + appMinor uint = 2 appPatch uint = 0 // appPreRelease MUST only contain characters from semanticAlphabet