github.com/ethereumproject/go-ethereum@v5.5.2+incompatible/cmd/geth/migrate_datadir.go (about) 1 // Copyright 2015 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package main 18 19 import ( 20 "fmt" 21 "math/big" 22 "os" 23 "path/filepath" 24 25 "github.com/ethereumproject/go-ethereum/common" 26 "github.com/ethereumproject/go-ethereum/core" 27 "github.com/ethereumproject/go-ethereum/ethdb" 28 "github.com/ethereumproject/go-ethereum/logger" 29 "github.com/ethereumproject/go-ethereum/logger/glog" 30 "gopkg.in/urfave/cli.v1" 31 ) 32 33 // handleIfDataDirSchemaMigrations is a handlers for the conditional logic around 34 // data/chain dir migrations from geth versions < 3.4 and in consideration of EF geth schemas used for ETC. 35 func handleIfDataDirSchemaMigrations(ctx *cli.Context) error { 36 37 origV := int(*glog.GetVerbosity()) 38 if origV == 0 { 39 origV = glog.DefaultVerbosity 40 } 41 42 // Turn verbosity down for migration check. If migration happens, it will print to Warn. 43 // Otherwise logs are just debuggers. 44 glog.SetToStderr(true) 45 glog.SetV(3) 46 47 if shouldAttemptDirMigration(ctx) { 48 // Rename existing default datadir <home>/<Ethereum>/ to <home>/<EthereumClassic>. 49 // Only do this if --datadir flag is not specified AND <home>/<EthereumClassic> does NOT already exist (only migrate once and only for defaulty). 50 // If it finds an 'Ethereum' directory, it will check if it contains default ETC or ETHF chain data. 51 // If it contains ETC data, it will rename the dir. If ETHF data, if will do nothing. 52 if err := migrateExistingDirToClassicNamingScheme(ctx); err != nil { 53 return err 54 } 55 56 // Move existing mainnet data to pertinent chain-named subdir scheme (ie ethereum-classic/mainnet). 57 // This should only happen if the given (newly defined in this protocol) subdir doesn't exist, 58 // and the dirs&files (nodekey, dapp, keystore, chaindata, nodes) do exist, 59 if err := migrateToChainSubdirIfNecessary(ctx); err != nil { 60 return err 61 } 62 } 63 // (Re)set default debug verbosity level. 64 glog.SetV(origV) 65 return nil 66 } 67 68 // migrateExistingDirToClassicNamingScheme renames default base data directory ".../Ethereum" to ".../EthereumClassic", pending os customs, etc... ;-) 69 /// 70 // Check for preexisting **Un-classic** data directory, ie "/home/path/to/Ethereum". 71 // If it exists, check if the data therein belongs to Classic blockchain (ie not configged as "ETF"), 72 // and rename it to fit Classic naming convention ("/home/path/to/EthereumClassic") if that dir doesn't already exist. 73 // This case only applies to Default, ie when a user **doesn't** provide a custom --datadir flag; 74 // a user should be able to override a specified data dir if they want. 75 func migrateExistingDirToClassicNamingScheme(ctx *cli.Context) error { 76 77 ethDataDirPath := common.DefaultUnclassicDataDir() 78 etcDataDirPath := common.DefaultDataDir() 79 80 // only if default <EthereumClassic>/ datadir doesn't already exist 81 if _, err := os.Stat(etcDataDirPath); err == nil { 82 // classic data dir already exists 83 glog.V(logger.Debug).Infof("Using existing ETClassic data directory at: %v\n", etcDataDirPath) 84 return nil 85 } 86 87 ethChainDBPath := filepath.Join(ethDataDirPath, "chaindata") 88 if chainIsMorden(ctx) { 89 ethChainDBPath = filepath.Join(ethDataDirPath, "testnet", "chaindata") 90 } 91 92 // only if ETHdatadir chaindb path DOES already exist, so return nil if it doesn't; 93 // otherwise NewLDBDatabase will create an empty one there. 94 // note that this uses the 'old' non-subdirectory way of holding default data. 95 // it must be called before migrating to subdirectories 96 // NOTE: Since ETH stores chaindata by default in Ethereum/geth/..., this path 97 // will not exist if the existing data belongs to ETH, so it works as a valid check for us as well. 98 if _, err := os.Stat(ethChainDBPath); os.IsNotExist(err) { 99 glog.V(logger.Debug).Warnf(`No existing default chaindata dir found at: %v 100 Using default data directory at: %v`, 101 ethChainDBPath, etcDataDirPath) 102 return nil 103 } 104 105 foundCorrectLookingFiles := []string{} 106 requiredFiles := []string{"LOG", "LOCK", "CURRENT"} 107 for _, f := range requiredFiles { 108 p := filepath.Join(ethChainDBPath, f) 109 if _, err := os.Stat(p); os.IsNotExist(err) { 110 glog.V(logger.Debug).Warnf(`No existing default file found at: %v 111 Using default data directory at: %v`, 112 p, etcDataDirPath) 113 } else { 114 foundCorrectLookingFiles = append(foundCorrectLookingFiles, f) 115 } 116 } 117 hasRequiredFiles := len(requiredFiles) == len(foundCorrectLookingFiles) 118 if !hasRequiredFiles { 119 return nil 120 } 121 122 // check if there is existing etf blockchain data in unclassic default dir (ie /<home>/Ethereum) 123 chainDB, err := ethdb.NewLDBDatabase(ethChainDBPath, 0, 0) 124 if err != nil { 125 glog.V(logger.Debug).Warnf(`Failed to check blockchain compatibility for existing Ethereum chaindata database at: %v 126 Using default data directory at: %v`, 127 err, etcDataDirPath) 128 return nil 129 } 130 131 defer chainDB.Close() 132 133 // Only move if defaulty ETC (mainnet or testnet). 134 // Get head block if testnet, fork block if mainnet. 135 hh := core.GetHeadBlockHash(chainDB) // get last block in fork 136 if ctx.GlobalBool(aliasableName(FastSyncFlag.Name, ctx)) { 137 hh = core.GetHeadFastBlockHash(chainDB) 138 } 139 if hh.IsEmpty() { 140 glog.V(logger.Debug).Warnln("There was no head block for the old database. It could be very young.") 141 } 142 143 hasRequiredForkIfSufficientHeight := true 144 if !hh.IsEmpty() { 145 // if head block < 1920000, then its compatible 146 // if head block >= 1920000, then it must have a hash matching required hash 147 148 // Use default configuration to check if known fork, if block 1920000 exists. 149 // If block1920000 doesn't exist, given above checks for directory structure expectations, 150 // I think it's safe to assume that the chaindata directory is just too 'young', where it hasn't 151 // synced until block 1920000, and therefore can be migrated. 152 conf := core.DefaultConfigMainnet.ChainConfig 153 if chainIsMorden(ctx) { 154 conf = core.DefaultConfigMorden.ChainConfig 155 } 156 157 hf := conf.ForkByName("The DAO Hard Fork") 158 if hf == nil || hf.Block == nil || new(big.Int).Cmp(hf.Block) == 0 || hf.RequiredHash.IsEmpty() { 159 glog.V(logger.Debug).Warnln("DAO Hard Fork required hash not configured for database chain. Not migrating.") 160 return nil 161 } 162 163 b := core.GetBlock(chainDB, hh) 164 if b == nil { 165 glog.V(logger.Debug).Warnf("There was a problem checking the head block of old-namespaced database. The head hash was: %v", hh.Hex()) 166 return nil 167 } 168 169 // if head block >= 1920000 170 if b.Number().Cmp(hf.Block) >= 0 { 171 // now, since we know that the height is bigger than the hardfork, we have to check that the db contains the required hardfork hash 172 glog.V(logger.Debug).Infof("Existing head block in old data dir has sufficient height: %v", b.String()) 173 174 hasRequiredForkIfSufficientHeight = false 175 bf := core.GetBlock(chainDB, hf.RequiredHash) 176 // does not have required block by hash 177 if bf != nil { 178 glog.V(logger.Debug).Infof("Head block has sufficient height AND required hash: %v", b.String()) 179 hasRequiredForkIfSufficientHeight = true 180 } else { 181 glog.V(logger.Debug).Infof("Head block has sufficient height but not required hash: %v", b.String()) 182 } 183 // head block < 1920000 184 } else { 185 glog.V(logger.Debug).Infof("Existing head block in old data dir has INSUFFICIENT height to differentiate ETC/ETF: %v", b.String()) 186 } 187 } 188 189 if hasRequiredForkIfSufficientHeight { 190 // if any of the LOG, LOCK, or CURRENT files are missing from old chaindata/, don't migrate 191 glog.V(logger.Warn).Warnf(`Found existing data directory named 'Ethereum' with default ETC chaindata. 192 Moving it from: %v, to: %v 193 To specify a different data directory use the '--datadir' flag.`, 194 ethDataDirPath, etcDataDirPath) 195 return os.Rename(ethDataDirPath, etcDataDirPath) 196 } 197 198 glog.V(logger.Debug).Infof(`Existing default Ethereum database at: %v isn't an Ethereum Classic default blockchain. 199 Will not migrate. 200 Using ETC chaindata database at: %v`, 201 ethDataDirPath, etcDataDirPath) 202 return nil 203 } 204 205 // migrateToChainSubdirIfNecessary migrates ".../EthereumClassic/nodes|chaindata|...|nodekey" --> ".../EthereumClassic/mainnet/nodes|chaindata|...|nodekey" 206 func migrateToChainSubdirIfNecessary(ctx *cli.Context) error { 207 chainIdentity := mustMakeChainIdentity(ctx) // "mainnet", "morden", "custom" 208 209 datapath := mustMakeDataDir(ctx) // ".../EthereumClassic/ | --datadir" 210 211 subdirPath := MustMakeChainDataDir(ctx) // ie, <EthereumClassic>/mainnet 212 213 // check if default subdir "mainnet" exits 214 // NOTE: this assumes that if the migration has been run once, the "mainnet" dir will exist and will have necessary datum inside it 215 subdirPathInfo, err := os.Stat(subdirPath) 216 if err == nil { 217 // dir already exists 218 return nil 219 } 220 if subdirPathInfo != nil && !subdirPathInfo.IsDir() { 221 return fmt.Errorf(`%v: found file named '%v' in EthereumClassic datadir, 222 which conflicts with default chain directory naming convention: %v`, ErrDirectoryStructure, chainIdentity, subdirPath) 223 } 224 225 // 3.3 testnet uses subdir '/testnet' 226 if core.ChainIdentitiesMorden[chainIdentity] { 227 exTestDir := filepath.Join(subdirPath, "../testnet") 228 exTestDirInfo, e := os.Stat(exTestDir) 229 if e != nil && os.IsNotExist(e) { 230 return nil // ex testnet dir doesn't exist 231 } 232 if !exTestDirInfo.IsDir() { 233 return nil // don't interfere with user *file* that won't be relevant for geth 234 } 235 return os.Rename(exTestDir, subdirPath) // /testnet -> /morden 236 } 237 238 // mkdir -p ".../mainnet" 239 if err := os.MkdirAll(subdirPath, 0755); err != nil { 240 return err 241 } 242 243 // move if existing (nodekey, dapp/, keystore/, chaindata/, nodes/) into new subdirectories 244 for _, dir := range []string{"dapp", "keystore", "chaindata", "nodes"} { 245 246 dirPath := filepath.Join(datapath, dir) 247 248 dirInfo, e := os.Stat(dirPath) 249 if e != nil && os.IsNotExist(e) { 250 continue // dir doesn't exist 251 } 252 if !dirInfo.IsDir() { 253 continue // don't interfere with user *file* that won't be relevant for geth 254 } 255 256 dirPathUnderSubdir := filepath.Join(subdirPath, dir) 257 if err := os.Rename(dirPath, dirPathUnderSubdir); err != nil { 258 return err 259 } 260 } 261 262 // ensure nodekey exists and is file (loop lets us stay consistent in form here, an keep options open for easy other files to include) 263 for _, file := range []string{"nodekey", "geth.ipc"} { 264 filePath := filepath.Join(datapath, file) 265 266 // ensure exists and is a file 267 fileInfo, e := os.Stat(filePath) 268 if e != nil && os.IsNotExist(e) { 269 continue 270 } 271 if fileInfo.IsDir() { 272 continue // don't interfere with user dirs that won't be relevant for geth 273 } 274 275 filePathUnderSubdir := filepath.Join(subdirPath, file) 276 if err := os.Rename(filePath, filePathUnderSubdir); err != nil { 277 return err 278 } 279 } 280 return nil 281 }