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