github.com/gilgames000/kcc-geth@v1.0.6/cmd/geth/dbcmd.go (about) 1 // Copyright 2020 The go-ethereum Authors 2 // This file is part of go-ethereum. 3 // 4 // go-ethereum is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU 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 // go-ethereum 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 General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. 16 17 package main 18 19 import ( 20 "fmt" 21 "os" 22 "path/filepath" 23 "time" 24 25 "github.com/ethereum/go-ethereum/cmd/utils" 26 "github.com/ethereum/go-ethereum/common" 27 "github.com/ethereum/go-ethereum/common/hexutil" 28 "github.com/ethereum/go-ethereum/console/prompt" 29 "github.com/ethereum/go-ethereum/core/rawdb" 30 "github.com/ethereum/go-ethereum/ethdb" 31 "github.com/ethereum/go-ethereum/ethdb/leveldb" 32 "github.com/ethereum/go-ethereum/log" 33 "github.com/syndtr/goleveldb/leveldb/opt" 34 "gopkg.in/urfave/cli.v1" 35 ) 36 37 var ( 38 removedbCommand = cli.Command{ 39 Action: utils.MigrateFlags(removeDB), 40 Name: "removedb", 41 Usage: "Remove blockchain and state databases", 42 ArgsUsage: "", 43 Flags: []cli.Flag{ 44 utils.DataDirFlag, 45 }, 46 Category: "DATABASE COMMANDS", 47 Description: ` 48 Remove blockchain and state databases`, 49 } 50 dbCommand = cli.Command{ 51 Name: "db", 52 Usage: "Low level database operations", 53 ArgsUsage: "", 54 Category: "DATABASE COMMANDS", 55 Subcommands: []cli.Command{ 56 dbInspectCmd, 57 dbStatCmd, 58 dbCompactCmd, 59 dbGetCmd, 60 dbDeleteCmd, 61 dbPutCmd, 62 }, 63 } 64 dbInspectCmd = cli.Command{ 65 Action: utils.MigrateFlags(inspect), 66 Name: "inspect", 67 ArgsUsage: "<prefix> <start>", 68 69 Usage: "Inspect the storage size for each type of data in the database", 70 Description: `This commands iterates the entire database. If the optional 'prefix' and 'start' arguments are provided, then the iteration is limited to the given subset of data.`, 71 } 72 dbStatCmd = cli.Command{ 73 Action: dbStats, 74 Name: "stats", 75 Usage: "Print leveldb statistics", 76 } 77 dbCompactCmd = cli.Command{ 78 Action: dbCompact, 79 Name: "compact", 80 Usage: "Compact leveldb database. WARNING: May take a very long time", 81 Description: `This command performs a database compaction. 82 WARNING: This operation may take a very long time to finish, and may cause database 83 corruption if it is aborted during execution'!`, 84 } 85 dbGetCmd = cli.Command{ 86 Action: dbGet, 87 Name: "get", 88 Usage: "Show the value of a database key", 89 ArgsUsage: "<hex-encoded key>", 90 Description: "This command looks up the specified database key from the database.", 91 } 92 dbDeleteCmd = cli.Command{ 93 Action: dbDelete, 94 Name: "delete", 95 Usage: "Delete a database key (WARNING: may corrupt your database)", 96 ArgsUsage: "<hex-encoded key>", 97 Description: `This command deletes the specified database key from the database. 98 WARNING: This is a low-level operation which may cause database corruption!`, 99 } 100 dbPutCmd = cli.Command{ 101 Action: dbPut, 102 Name: "put", 103 Usage: "Set the value of a database key (WARNING: may corrupt your database)", 104 ArgsUsage: "<hex-encoded key> <hex-encoded value>", 105 Description: `This command sets a given database key to the given value. 106 WARNING: This is a low-level operation which may cause database corruption!`, 107 } 108 ) 109 110 func removeDB(ctx *cli.Context) error { 111 stack, config := makeConfigNode(ctx) 112 113 // Remove the full node state database 114 path := stack.ResolvePath("chaindata") 115 if common.FileExist(path) { 116 confirmAndRemoveDB(path, "full node state database") 117 } else { 118 log.Info("Full node state database missing", "path", path) 119 } 120 // Remove the full node ancient database 121 path = config.Eth.DatabaseFreezer 122 switch { 123 case path == "": 124 path = filepath.Join(stack.ResolvePath("chaindata"), "ancient") 125 case !filepath.IsAbs(path): 126 path = config.Node.ResolvePath(path) 127 } 128 if common.FileExist(path) { 129 confirmAndRemoveDB(path, "full node ancient database") 130 } else { 131 log.Info("Full node ancient database missing", "path", path) 132 } 133 // Remove the light node database 134 path = stack.ResolvePath("lightchaindata") 135 if common.FileExist(path) { 136 confirmAndRemoveDB(path, "light node database") 137 } else { 138 log.Info("Light node database missing", "path", path) 139 } 140 return nil 141 } 142 143 // confirmAndRemoveDB prompts the user for a last confirmation and removes the 144 // folder if accepted. 145 func confirmAndRemoveDB(database string, kind string) { 146 confirm, err := prompt.Stdin.PromptConfirm(fmt.Sprintf("Remove %s (%s)?", kind, database)) 147 switch { 148 case err != nil: 149 utils.Fatalf("%v", err) 150 case !confirm: 151 log.Info("Database deletion skipped", "path", database) 152 default: 153 start := time.Now() 154 filepath.Walk(database, func(path string, info os.FileInfo, err error) error { 155 // If we're at the top level folder, recurse into 156 if path == database { 157 return nil 158 } 159 // Delete all the files, but not subfolders 160 if !info.IsDir() { 161 os.Remove(path) 162 return nil 163 } 164 return filepath.SkipDir 165 }) 166 log.Info("Database successfully deleted", "path", database, "elapsed", common.PrettyDuration(time.Since(start))) 167 } 168 } 169 170 func inspect(ctx *cli.Context) error { 171 var ( 172 prefix []byte 173 start []byte 174 ) 175 if ctx.NArg() > 2 { 176 return fmt.Errorf("Max 2 arguments: %v", ctx.Command.ArgsUsage) 177 } 178 if ctx.NArg() >= 1 { 179 if d, err := hexutil.Decode(ctx.Args().Get(0)); err != nil { 180 return fmt.Errorf("failed to hex-decode 'prefix': %v", err) 181 } else { 182 prefix = d 183 } 184 } 185 if ctx.NArg() >= 2 { 186 if d, err := hexutil.Decode(ctx.Args().Get(1)); err != nil { 187 return fmt.Errorf("failed to hex-decode 'start': %v", err) 188 } else { 189 start = d 190 } 191 } 192 stack, _ := makeConfigNode(ctx) 193 defer stack.Close() 194 195 _, chainDb := utils.MakeChain(ctx, stack, true) 196 defer chainDb.Close() 197 198 return rawdb.InspectDatabase(chainDb, prefix, start) 199 } 200 201 func showLeveldbStats(db ethdb.Stater) { 202 if stats, err := db.Stat("leveldb.stats"); err != nil { 203 log.Warn("Failed to read database stats", "error", err) 204 } else { 205 fmt.Println(stats) 206 } 207 if ioStats, err := db.Stat("leveldb.iostats"); err != nil { 208 log.Warn("Failed to read database iostats", "error", err) 209 } else { 210 fmt.Println(ioStats) 211 } 212 } 213 214 func dbStats(ctx *cli.Context) error { 215 stack, _ := makeConfigNode(ctx) 216 defer stack.Close() 217 path := stack.ResolvePath("chaindata") 218 db, err := leveldb.NewCustom(path, "", func(options *opt.Options) { 219 options.ReadOnly = true 220 }) 221 if err != nil { 222 return err 223 } 224 showLeveldbStats(db) 225 err = db.Close() 226 if err != nil { 227 log.Info("Close err", "error", err) 228 } 229 return nil 230 } 231 232 func dbCompact(ctx *cli.Context) error { 233 stack, _ := makeConfigNode(ctx) 234 defer stack.Close() 235 path := stack.ResolvePath("chaindata") 236 cache := ctx.GlobalInt(utils.CacheFlag.Name) * ctx.GlobalInt(utils.CacheDatabaseFlag.Name) / 100 237 db, err := leveldb.NewCustom(path, "", func(options *opt.Options) { 238 options.OpenFilesCacheCapacity = utils.MakeDatabaseHandles() 239 options.BlockCacheCapacity = cache / 2 * opt.MiB 240 options.WriteBuffer = cache / 4 * opt.MiB // Two of these are used internally 241 }) 242 if err != nil { 243 return err 244 } 245 showLeveldbStats(db) 246 log.Info("Triggering compaction") 247 err = db.Compact(nil, nil) 248 if err != nil { 249 log.Info("Compact err", "error", err) 250 } 251 showLeveldbStats(db) 252 log.Info("Closing db") 253 err = db.Close() 254 if err != nil { 255 log.Info("Close err", "error", err) 256 } 257 log.Info("Exiting") 258 return err 259 } 260 261 // dbGet shows the value of a given database key 262 func dbGet(ctx *cli.Context) error { 263 if ctx.NArg() != 1 { 264 return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage) 265 } 266 stack, _ := makeConfigNode(ctx) 267 defer stack.Close() 268 path := stack.ResolvePath("chaindata") 269 db, err := leveldb.NewCustom(path, "", func(options *opt.Options) { 270 options.ReadOnly = true 271 }) 272 if err != nil { 273 return err 274 } 275 defer db.Close() 276 key, err := hexutil.Decode(ctx.Args().Get(0)) 277 if err != nil { 278 log.Info("Could not decode the key", "error", err) 279 return err 280 } 281 data, err := db.Get(key) 282 if err != nil { 283 log.Info("Get operation failed", "error", err) 284 return err 285 } 286 fmt.Printf("key %#x:\n\t%#x\n", key, data) 287 return nil 288 } 289 290 // dbDelete deletes a key from the database 291 func dbDelete(ctx *cli.Context) error { 292 if ctx.NArg() != 1 { 293 return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage) 294 } 295 stack, _ := makeConfigNode(ctx) 296 defer stack.Close() 297 db := utils.MakeChainDatabase(ctx, stack) 298 defer db.Close() 299 key, err := hexutil.Decode(ctx.Args().Get(0)) 300 if err != nil { 301 log.Info("Could not decode the key", "error", err) 302 return err 303 } 304 if err = db.Delete(key); err != nil { 305 log.Info("Delete operation returned an error", "error", err) 306 return err 307 } 308 return nil 309 } 310 311 // dbPut overwrite a value in the database 312 func dbPut(ctx *cli.Context) error { 313 if ctx.NArg() != 2 { 314 return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage) 315 } 316 stack, _ := makeConfigNode(ctx) 317 defer stack.Close() 318 db := utils.MakeChainDatabase(ctx, stack) 319 defer db.Close() 320 var ( 321 key []byte 322 value []byte 323 data []byte 324 err error 325 ) 326 key, err = hexutil.Decode(ctx.Args().Get(0)) 327 if err != nil { 328 log.Info("Could not decode the key", "error", err) 329 return err 330 } 331 value, err = hexutil.Decode(ctx.Args().Get(1)) 332 if err != nil { 333 log.Info("Could not decode the value", "error", err) 334 return err 335 } 336 data, err = db.Get(key) 337 if err == nil { 338 fmt.Printf("Previous value:\n%#x\n", data) 339 } 340 return db.Put(key, value) 341 }