github.com/DxChainNetwork/dxc@v0.8.1-0.20220824085222-1162e304b6e7/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 "sort" 24 "strconv" 25 "time" 26 27 "github.com/DxChainNetwork/dxc/cmd/utils" 28 "github.com/DxChainNetwork/dxc/common" 29 "github.com/DxChainNetwork/dxc/common/hexutil" 30 "github.com/DxChainNetwork/dxc/console/prompt" 31 "github.com/DxChainNetwork/dxc/core/rawdb" 32 "github.com/DxChainNetwork/dxc/ethdb" 33 "github.com/DxChainNetwork/dxc/log" 34 "github.com/DxChainNetwork/dxc/trie" 35 "gopkg.in/urfave/cli.v1" 36 ) 37 38 var ( 39 removedbCommand = cli.Command{ 40 Action: utils.MigrateFlags(removeDB), 41 Name: "removedb", 42 Usage: "Remove blockchain and state databases", 43 ArgsUsage: "", 44 Flags: []cli.Flag{ 45 utils.DataDirFlag, 46 }, 47 Category: "DATABASE COMMANDS", 48 Description: ` 49 Remove blockchain and state databases`, 50 } 51 dbCommand = cli.Command{ 52 Name: "db", 53 Usage: "Low level database operations", 54 ArgsUsage: "", 55 Category: "DATABASE COMMANDS", 56 Subcommands: []cli.Command{ 57 dbInspectCmd, 58 dbStatCmd, 59 dbCompactCmd, 60 dbGetCmd, 61 dbDeleteCmd, 62 dbPutCmd, 63 dbGetSlotsCmd, 64 dbDumpFreezerIndex, 65 }, 66 } 67 dbInspectCmd = cli.Command{ 68 Action: utils.MigrateFlags(inspect), 69 Name: "inspect", 70 ArgsUsage: "<prefix> <start>", 71 Flags: []cli.Flag{ 72 utils.DataDirFlag, 73 utils.SyncModeFlag, 74 utils.MainnetFlag, 75 utils.TestnetFlag, 76 }, 77 Usage: "Inspect the storage size for each type of data in the database", 78 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.`, 79 } 80 dbStatCmd = cli.Command{ 81 Action: utils.MigrateFlags(dbStats), 82 Name: "stats", 83 Usage: "Print leveldb statistics", 84 Flags: []cli.Flag{ 85 utils.DataDirFlag, 86 utils.SyncModeFlag, 87 utils.MainnetFlag, 88 utils.TestnetFlag, 89 }, 90 } 91 dbCompactCmd = cli.Command{ 92 Action: utils.MigrateFlags(dbCompact), 93 Name: "compact", 94 Usage: "Compact leveldb database. WARNING: May take a very long time", 95 Flags: []cli.Flag{ 96 utils.DataDirFlag, 97 utils.SyncModeFlag, 98 utils.MainnetFlag, 99 utils.TestnetFlag, 100 utils.CacheFlag, 101 utils.CacheDatabaseFlag, 102 }, 103 Description: `This command performs a database compaction. 104 WARNING: This operation may take a very long time to finish, and may cause database 105 corruption if it is aborted during execution'!`, 106 } 107 dbGetCmd = cli.Command{ 108 Action: utils.MigrateFlags(dbGet), 109 Name: "get", 110 Usage: "Show the value of a database key", 111 ArgsUsage: "<hex-encoded key>", 112 Flags: []cli.Flag{ 113 utils.DataDirFlag, 114 utils.SyncModeFlag, 115 utils.MainnetFlag, 116 utils.TestnetFlag, 117 }, 118 Description: "This command looks up the specified database key from the database.", 119 } 120 dbDeleteCmd = cli.Command{ 121 Action: utils.MigrateFlags(dbDelete), 122 Name: "delete", 123 Usage: "Delete a database key (WARNING: may corrupt your database)", 124 ArgsUsage: "<hex-encoded key>", 125 Flags: []cli.Flag{ 126 utils.DataDirFlag, 127 utils.SyncModeFlag, 128 utils.MainnetFlag, 129 utils.TestnetFlag, 130 }, 131 Description: `This command deletes the specified database key from the database. 132 WARNING: This is a low-level operation which may cause database corruption!`, 133 } 134 dbPutCmd = cli.Command{ 135 Action: utils.MigrateFlags(dbPut), 136 Name: "put", 137 Usage: "Set the value of a database key (WARNING: may corrupt your database)", 138 ArgsUsage: "<hex-encoded key> <hex-encoded value>", 139 Flags: []cli.Flag{ 140 utils.DataDirFlag, 141 utils.SyncModeFlag, 142 utils.MainnetFlag, 143 utils.TestnetFlag, 144 }, 145 Description: `This command sets a given database key to the given value. 146 WARNING: This is a low-level operation which may cause database corruption!`, 147 } 148 dbGetSlotsCmd = cli.Command{ 149 Action: utils.MigrateFlags(dbDumpTrie), 150 Name: "dumptrie", 151 Usage: "Show the storage key/values of a given storage trie", 152 ArgsUsage: "<hex-encoded storage trie root> <hex-encoded start (optional)> <int max elements (optional)>", 153 Flags: []cli.Flag{ 154 utils.DataDirFlag, 155 utils.SyncModeFlag, 156 utils.MainnetFlag, 157 utils.TestnetFlag, 158 }, 159 Description: "This command looks up the specified database key from the database.", 160 } 161 dbDumpFreezerIndex = cli.Command{ 162 Action: utils.MigrateFlags(freezerInspect), 163 Name: "freezer-index", 164 Usage: "Dump out the index of a given freezer type", 165 ArgsUsage: "<type> <start (int)> <end (int)>", 166 Flags: []cli.Flag{ 167 utils.DataDirFlag, 168 utils.SyncModeFlag, 169 utils.MainnetFlag, 170 utils.TestnetFlag, 171 }, 172 Description: "This command displays information about the freezer index.", 173 } 174 ) 175 176 func removeDB(ctx *cli.Context) error { 177 stack, config := makeConfigNode(ctx) 178 179 // Remove the full node state database 180 path := stack.ResolvePath("chaindata") 181 if common.FileExist(path) { 182 confirmAndRemoveDB(path, "full node state database") 183 } else { 184 log.Info("Full node state database missing", "path", path) 185 } 186 // Remove the full node ancient database 187 path = config.Eth.DatabaseFreezer 188 switch { 189 case path == "": 190 path = filepath.Join(stack.ResolvePath("chaindata"), "ancient") 191 case !filepath.IsAbs(path): 192 path = config.Node.ResolvePath(path) 193 } 194 if common.FileExist(path) { 195 confirmAndRemoveDB(path, "full node ancient database") 196 } else { 197 log.Info("Full node ancient database missing", "path", path) 198 } 199 // Remove the light node database 200 path = stack.ResolvePath("lightchaindata") 201 if common.FileExist(path) { 202 confirmAndRemoveDB(path, "light node database") 203 } else { 204 log.Info("Light node database missing", "path", path) 205 } 206 return nil 207 } 208 209 // confirmAndRemoveDB prompts the user for a last confirmation and removes the 210 // folder if accepted. 211 func confirmAndRemoveDB(database string, kind string) { 212 confirm, err := prompt.Stdin.PromptConfirm(fmt.Sprintf("Remove %s (%s)?", kind, database)) 213 switch { 214 case err != nil: 215 utils.Fatalf("%v", err) 216 case !confirm: 217 log.Info("Database deletion skipped", "path", database) 218 default: 219 start := time.Now() 220 filepath.Walk(database, func(path string, info os.FileInfo, err error) error { 221 // If we're at the top level folder, recurse into 222 if path == database { 223 return nil 224 } 225 // Delete all the files, but not subfolders 226 if !info.IsDir() { 227 os.Remove(path) 228 return nil 229 } 230 return filepath.SkipDir 231 }) 232 log.Info("Database successfully deleted", "path", database, "elapsed", common.PrettyDuration(time.Since(start))) 233 } 234 } 235 236 func inspect(ctx *cli.Context) error { 237 var ( 238 prefix []byte 239 start []byte 240 ) 241 if ctx.NArg() > 2 { 242 return fmt.Errorf("Max 2 arguments: %v", ctx.Command.ArgsUsage) 243 } 244 if ctx.NArg() >= 1 { 245 if d, err := hexutil.Decode(ctx.Args().Get(0)); err != nil { 246 return fmt.Errorf("failed to hex-decode 'prefix': %v", err) 247 } else { 248 prefix = d 249 } 250 } 251 if ctx.NArg() >= 2 { 252 if d, err := hexutil.Decode(ctx.Args().Get(1)); err != nil { 253 return fmt.Errorf("failed to hex-decode 'start': %v", err) 254 } else { 255 start = d 256 } 257 } 258 stack, _ := makeConfigNode(ctx) 259 defer stack.Close() 260 261 db := utils.MakeChainDatabase(ctx, stack, true) 262 defer db.Close() 263 264 return rawdb.InspectDatabase(db, prefix, start) 265 } 266 267 func showLeveldbStats(db ethdb.Stater) { 268 if stats, err := db.Stat("leveldb.stats"); err != nil { 269 log.Warn("Failed to read database stats", "error", err) 270 } else { 271 fmt.Println(stats) 272 } 273 if ioStats, err := db.Stat("leveldb.iostats"); err != nil { 274 log.Warn("Failed to read database iostats", "error", err) 275 } else { 276 fmt.Println(ioStats) 277 } 278 } 279 280 func dbStats(ctx *cli.Context) error { 281 stack, _ := makeConfigNode(ctx) 282 defer stack.Close() 283 284 db := utils.MakeChainDatabase(ctx, stack, true) 285 defer db.Close() 286 287 showLeveldbStats(db) 288 return nil 289 } 290 291 func dbCompact(ctx *cli.Context) error { 292 stack, _ := makeConfigNode(ctx) 293 defer stack.Close() 294 295 db := utils.MakeChainDatabase(ctx, stack, false) 296 defer db.Close() 297 298 log.Info("Stats before compaction") 299 showLeveldbStats(db) 300 301 log.Info("Triggering compaction") 302 if err := db.Compact(nil, nil); err != nil { 303 log.Info("Compact err", "error", err) 304 return err 305 } 306 log.Info("Stats after compaction") 307 showLeveldbStats(db) 308 return nil 309 } 310 311 // dbGet shows the value of a given database key 312 func dbGet(ctx *cli.Context) error { 313 if ctx.NArg() != 1 { 314 return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage) 315 } 316 stack, _ := makeConfigNode(ctx) 317 defer stack.Close() 318 319 db := utils.MakeChainDatabase(ctx, stack, true) 320 defer db.Close() 321 322 key, err := hexutil.Decode(ctx.Args().Get(0)) 323 if err != nil { 324 log.Info("Could not decode the key", "error", err) 325 return err 326 } 327 data, err := db.Get(key) 328 if err != nil { 329 log.Info("Get operation failed", "error", err) 330 return err 331 } 332 fmt.Printf("key %#x: %#x\n", key, data) 333 return nil 334 } 335 336 // dbDelete deletes a key from the database 337 func dbDelete(ctx *cli.Context) error { 338 if ctx.NArg() != 1 { 339 return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage) 340 } 341 stack, _ := makeConfigNode(ctx) 342 defer stack.Close() 343 344 db := utils.MakeChainDatabase(ctx, stack, false) 345 defer db.Close() 346 347 key, err := hexutil.Decode(ctx.Args().Get(0)) 348 if err != nil { 349 log.Info("Could not decode the key", "error", err) 350 return err 351 } 352 data, err := db.Get(key) 353 if err == nil { 354 fmt.Printf("Previous value: %#x\n", data) 355 } 356 if err = db.Delete(key); err != nil { 357 log.Info("Delete operation returned an error", "error", err) 358 return err 359 } 360 return nil 361 } 362 363 // dbPut overwrite a value in the database 364 func dbPut(ctx *cli.Context) error { 365 if ctx.NArg() != 2 { 366 return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage) 367 } 368 stack, _ := makeConfigNode(ctx) 369 defer stack.Close() 370 371 db := utils.MakeChainDatabase(ctx, stack, false) 372 defer db.Close() 373 374 var ( 375 key []byte 376 value []byte 377 data []byte 378 err error 379 ) 380 key, err = hexutil.Decode(ctx.Args().Get(0)) 381 if err != nil { 382 log.Info("Could not decode the key", "error", err) 383 return err 384 } 385 value, err = hexutil.Decode(ctx.Args().Get(1)) 386 if err != nil { 387 log.Info("Could not decode the value", "error", err) 388 return err 389 } 390 data, err = db.Get(key) 391 if err == nil { 392 fmt.Printf("Previous value: %#x\n", data) 393 } 394 return db.Put(key, value) 395 } 396 397 // dbDumpTrie shows the key-value slots of a given storage trie 398 func dbDumpTrie(ctx *cli.Context) error { 399 if ctx.NArg() < 1 { 400 return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage) 401 } 402 stack, _ := makeConfigNode(ctx) 403 defer stack.Close() 404 405 db := utils.MakeChainDatabase(ctx, stack, true) 406 defer db.Close() 407 var ( 408 root []byte 409 start []byte 410 max = int64(-1) 411 err error 412 ) 413 if root, err = hexutil.Decode(ctx.Args().Get(0)); err != nil { 414 log.Info("Could not decode the root", "error", err) 415 return err 416 } 417 stRoot := common.BytesToHash(root) 418 if ctx.NArg() >= 2 { 419 if start, err = hexutil.Decode(ctx.Args().Get(1)); err != nil { 420 log.Info("Could not decode the seek position", "error", err) 421 return err 422 } 423 } 424 if ctx.NArg() >= 3 { 425 if max, err = strconv.ParseInt(ctx.Args().Get(2), 10, 64); err != nil { 426 log.Info("Could not decode the max count", "error", err) 427 return err 428 } 429 } 430 theTrie, err := trie.New(stRoot, trie.NewDatabase(db)) 431 if err != nil { 432 return err 433 } 434 var count int64 435 it := trie.NewIterator(theTrie.NodeIterator(start)) 436 for it.Next() { 437 if max > 0 && count == max { 438 fmt.Printf("Exiting after %d values\n", count) 439 break 440 } 441 fmt.Printf(" %d. key %#x: %#x\n", count, it.Key, it.Value) 442 count++ 443 } 444 return it.Err 445 } 446 447 func freezerInspect(ctx *cli.Context) error { 448 var ( 449 start, end int64 450 disableSnappy bool 451 err error 452 ) 453 if ctx.NArg() < 3 { 454 return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage) 455 } 456 kind := ctx.Args().Get(0) 457 if noSnap, ok := rawdb.FreezerNoSnappy[kind]; !ok { 458 var options []string 459 for opt := range rawdb.FreezerNoSnappy { 460 options = append(options, opt) 461 } 462 sort.Strings(options) 463 return fmt.Errorf("Could read freezer-type '%v'. Available options: %v", kind, options) 464 } else { 465 disableSnappy = noSnap 466 } 467 if start, err = strconv.ParseInt(ctx.Args().Get(1), 10, 64); err != nil { 468 log.Info("Could read start-param", "error", err) 469 return err 470 } 471 if end, err = strconv.ParseInt(ctx.Args().Get(2), 10, 64); err != nil { 472 log.Info("Could read count param", "error", err) 473 return err 474 } 475 stack, _ := makeConfigNode(ctx) 476 defer stack.Close() 477 path := filepath.Join(stack.ResolvePath("chaindata"), "ancient") 478 log.Info("Opening freezer", "location", path, "name", kind) 479 if f, err := rawdb.NewFreezerTable(path, kind, disableSnappy); err != nil { 480 return err 481 } else { 482 f.DumpIndex(start, end) 483 } 484 return nil 485 }