github.com/LampardNguyen234/go-ethereum@v1.10.16-0.20220117140830-b6a3b0260724/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 "bytes" 21 "errors" 22 "fmt" 23 "os" 24 "os/signal" 25 "path/filepath" 26 "sort" 27 "strconv" 28 "strings" 29 "syscall" 30 "time" 31 32 "github.com/LampardNguyen234/go-ethereum/cmd/utils" 33 "github.com/LampardNguyen234/go-ethereum/common" 34 "github.com/LampardNguyen234/go-ethereum/common/hexutil" 35 "github.com/LampardNguyen234/go-ethereum/console/prompt" 36 "github.com/LampardNguyen234/go-ethereum/core/rawdb" 37 "github.com/LampardNguyen234/go-ethereum/ethdb" 38 "github.com/LampardNguyen234/go-ethereum/log" 39 "github.com/LampardNguyen234/go-ethereum/trie" 40 "gopkg.in/urfave/cli.v1" 41 ) 42 43 var ( 44 removedbCommand = cli.Command{ 45 Action: utils.MigrateFlags(removeDB), 46 Name: "removedb", 47 Usage: "Remove blockchain and state databases", 48 ArgsUsage: "", 49 Flags: []cli.Flag{ 50 utils.DataDirFlag, 51 }, 52 Category: "DATABASE COMMANDS", 53 Description: ` 54 Remove blockchain and state databases`, 55 } 56 dbCommand = cli.Command{ 57 Name: "db", 58 Usage: "Low level database operations", 59 ArgsUsage: "", 60 Category: "DATABASE COMMANDS", 61 Subcommands: []cli.Command{ 62 dbInspectCmd, 63 dbStatCmd, 64 dbCompactCmd, 65 dbGetCmd, 66 dbDeleteCmd, 67 dbPutCmd, 68 dbGetSlotsCmd, 69 dbDumpFreezerIndex, 70 dbImportCmd, 71 dbExportCmd, 72 }, 73 } 74 dbInspectCmd = cli.Command{ 75 Action: utils.MigrateFlags(inspect), 76 Name: "inspect", 77 ArgsUsage: "<prefix> <start>", 78 Flags: []cli.Flag{ 79 utils.DataDirFlag, 80 utils.AncientFlag, 81 utils.SyncModeFlag, 82 utils.MainnetFlag, 83 utils.RopstenFlag, 84 utils.SepoliaFlag, 85 utils.RinkebyFlag, 86 utils.GoerliFlag, 87 }, 88 Usage: "Inspect the storage size for each type of data in the database", 89 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.`, 90 } 91 dbStatCmd = cli.Command{ 92 Action: utils.MigrateFlags(dbStats), 93 Name: "stats", 94 Usage: "Print leveldb statistics", 95 Flags: []cli.Flag{ 96 utils.DataDirFlag, 97 utils.SyncModeFlag, 98 utils.MainnetFlag, 99 utils.RopstenFlag, 100 utils.SepoliaFlag, 101 utils.RinkebyFlag, 102 utils.GoerliFlag, 103 }, 104 } 105 dbCompactCmd = cli.Command{ 106 Action: utils.MigrateFlags(dbCompact), 107 Name: "compact", 108 Usage: "Compact leveldb database. WARNING: May take a very long time", 109 Flags: []cli.Flag{ 110 utils.DataDirFlag, 111 utils.SyncModeFlag, 112 utils.MainnetFlag, 113 utils.RopstenFlag, 114 utils.SepoliaFlag, 115 utils.RinkebyFlag, 116 utils.GoerliFlag, 117 utils.CacheFlag, 118 utils.CacheDatabaseFlag, 119 }, 120 Description: `This command performs a database compaction. 121 WARNING: This operation may take a very long time to finish, and may cause database 122 corruption if it is aborted during execution'!`, 123 } 124 dbGetCmd = cli.Command{ 125 Action: utils.MigrateFlags(dbGet), 126 Name: "get", 127 Usage: "Show the value of a database key", 128 ArgsUsage: "<hex-encoded key>", 129 Flags: []cli.Flag{ 130 utils.DataDirFlag, 131 utils.SyncModeFlag, 132 utils.MainnetFlag, 133 utils.RopstenFlag, 134 utils.SepoliaFlag, 135 utils.RinkebyFlag, 136 utils.GoerliFlag, 137 }, 138 Description: "This command looks up the specified database key from the database.", 139 } 140 dbDeleteCmd = cli.Command{ 141 Action: utils.MigrateFlags(dbDelete), 142 Name: "delete", 143 Usage: "Delete a database key (WARNING: may corrupt your database)", 144 ArgsUsage: "<hex-encoded key>", 145 Flags: []cli.Flag{ 146 utils.DataDirFlag, 147 utils.SyncModeFlag, 148 utils.MainnetFlag, 149 utils.RopstenFlag, 150 utils.SepoliaFlag, 151 utils.RinkebyFlag, 152 utils.GoerliFlag, 153 }, 154 Description: `This command deletes the specified database key from the database. 155 WARNING: This is a low-level operation which may cause database corruption!`, 156 } 157 dbPutCmd = cli.Command{ 158 Action: utils.MigrateFlags(dbPut), 159 Name: "put", 160 Usage: "Set the value of a database key (WARNING: may corrupt your database)", 161 ArgsUsage: "<hex-encoded key> <hex-encoded value>", 162 Flags: []cli.Flag{ 163 utils.DataDirFlag, 164 utils.SyncModeFlag, 165 utils.MainnetFlag, 166 utils.RopstenFlag, 167 utils.SepoliaFlag, 168 utils.RinkebyFlag, 169 utils.GoerliFlag, 170 }, 171 Description: `This command sets a given database key to the given value. 172 WARNING: This is a low-level operation which may cause database corruption!`, 173 } 174 dbGetSlotsCmd = cli.Command{ 175 Action: utils.MigrateFlags(dbDumpTrie), 176 Name: "dumptrie", 177 Usage: "Show the storage key/values of a given storage trie", 178 ArgsUsage: "<hex-encoded storage trie root> <hex-encoded start (optional)> <int max elements (optional)>", 179 Flags: []cli.Flag{ 180 utils.DataDirFlag, 181 utils.SyncModeFlag, 182 utils.MainnetFlag, 183 utils.RopstenFlag, 184 utils.SepoliaFlag, 185 utils.RinkebyFlag, 186 utils.GoerliFlag, 187 }, 188 Description: "This command looks up the specified database key from the database.", 189 } 190 dbDumpFreezerIndex = cli.Command{ 191 Action: utils.MigrateFlags(freezerInspect), 192 Name: "freezer-index", 193 Usage: "Dump out the index of a given freezer type", 194 ArgsUsage: "<type> <start (int)> <end (int)>", 195 Flags: []cli.Flag{ 196 utils.DataDirFlag, 197 utils.SyncModeFlag, 198 utils.MainnetFlag, 199 utils.RopstenFlag, 200 utils.SepoliaFlag, 201 utils.RinkebyFlag, 202 utils.GoerliFlag, 203 }, 204 Description: "This command displays information about the freezer index.", 205 } 206 dbImportCmd = cli.Command{ 207 Action: utils.MigrateFlags(importLDBdata), 208 Name: "import", 209 Usage: "Imports leveldb-data from an exported RLP dump.", 210 ArgsUsage: "<dumpfile> <start (optional)", 211 Flags: []cli.Flag{ 212 utils.DataDirFlag, 213 utils.SyncModeFlag, 214 utils.MainnetFlag, 215 utils.RopstenFlag, 216 utils.RinkebyFlag, 217 utils.GoerliFlag, 218 }, 219 Description: "The import command imports the specific chain data from an RLP encoded stream.", 220 } 221 dbExportCmd = cli.Command{ 222 Action: utils.MigrateFlags(exportChaindata), 223 Name: "export", 224 Usage: "Exports the chain data into an RLP dump. If the <dumpfile> has .gz suffix, gzip compression will be used.", 225 ArgsUsage: "<type> <dumpfile>", 226 Flags: []cli.Flag{ 227 utils.DataDirFlag, 228 utils.SyncModeFlag, 229 utils.MainnetFlag, 230 utils.RopstenFlag, 231 utils.RinkebyFlag, 232 utils.GoerliFlag, 233 }, 234 Description: "Exports the specified chain data to an RLP encoded stream, optionally gzip-compressed.", 235 } 236 ) 237 238 func removeDB(ctx *cli.Context) error { 239 stack, config := makeConfigNode(ctx) 240 241 // Remove the full node state database 242 path := stack.ResolvePath("chaindata") 243 if common.FileExist(path) { 244 confirmAndRemoveDB(path, "full node state database") 245 } else { 246 log.Info("Full node state database missing", "path", path) 247 } 248 // Remove the full node ancient database 249 path = config.Eth.DatabaseFreezer 250 switch { 251 case path == "": 252 path = filepath.Join(stack.ResolvePath("chaindata"), "ancient") 253 case !filepath.IsAbs(path): 254 path = config.Node.ResolvePath(path) 255 } 256 if common.FileExist(path) { 257 confirmAndRemoveDB(path, "full node ancient database") 258 } else { 259 log.Info("Full node ancient database missing", "path", path) 260 } 261 // Remove the light node database 262 path = stack.ResolvePath("lightchaindata") 263 if common.FileExist(path) { 264 confirmAndRemoveDB(path, "light node database") 265 } else { 266 log.Info("Light node database missing", "path", path) 267 } 268 return nil 269 } 270 271 // confirmAndRemoveDB prompts the user for a last confirmation and removes the 272 // folder if accepted. 273 func confirmAndRemoveDB(database string, kind string) { 274 confirm, err := prompt.Stdin.PromptConfirm(fmt.Sprintf("Remove %s (%s)?", kind, database)) 275 switch { 276 case err != nil: 277 utils.Fatalf("%v", err) 278 case !confirm: 279 log.Info("Database deletion skipped", "path", database) 280 default: 281 start := time.Now() 282 filepath.Walk(database, func(path string, info os.FileInfo, err error) error { 283 // If we're at the top level folder, recurse into 284 if path == database { 285 return nil 286 } 287 // Delete all the files, but not subfolders 288 if !info.IsDir() { 289 os.Remove(path) 290 return nil 291 } 292 return filepath.SkipDir 293 }) 294 log.Info("Database successfully deleted", "path", database, "elapsed", common.PrettyDuration(time.Since(start))) 295 } 296 } 297 298 func inspect(ctx *cli.Context) error { 299 var ( 300 prefix []byte 301 start []byte 302 ) 303 if ctx.NArg() > 2 { 304 return fmt.Errorf("Max 2 arguments: %v", ctx.Command.ArgsUsage) 305 } 306 if ctx.NArg() >= 1 { 307 if d, err := hexutil.Decode(ctx.Args().Get(0)); err != nil { 308 return fmt.Errorf("failed to hex-decode 'prefix': %v", err) 309 } else { 310 prefix = d 311 } 312 } 313 if ctx.NArg() >= 2 { 314 if d, err := hexutil.Decode(ctx.Args().Get(1)); err != nil { 315 return fmt.Errorf("failed to hex-decode 'start': %v", err) 316 } else { 317 start = d 318 } 319 } 320 stack, _ := makeConfigNode(ctx) 321 defer stack.Close() 322 323 db := utils.MakeChainDatabase(ctx, stack, true) 324 defer db.Close() 325 326 return rawdb.InspectDatabase(db, prefix, start) 327 } 328 329 func showLeveldbStats(db ethdb.Stater) { 330 if stats, err := db.Stat("leveldb.stats"); err != nil { 331 log.Warn("Failed to read database stats", "error", err) 332 } else { 333 fmt.Println(stats) 334 } 335 if ioStats, err := db.Stat("leveldb.iostats"); err != nil { 336 log.Warn("Failed to read database iostats", "error", err) 337 } else { 338 fmt.Println(ioStats) 339 } 340 } 341 342 func dbStats(ctx *cli.Context) error { 343 stack, _ := makeConfigNode(ctx) 344 defer stack.Close() 345 346 db := utils.MakeChainDatabase(ctx, stack, true) 347 defer db.Close() 348 349 showLeveldbStats(db) 350 return nil 351 } 352 353 func dbCompact(ctx *cli.Context) error { 354 stack, _ := makeConfigNode(ctx) 355 defer stack.Close() 356 357 db := utils.MakeChainDatabase(ctx, stack, false) 358 defer db.Close() 359 360 log.Info("Stats before compaction") 361 showLeveldbStats(db) 362 363 log.Info("Triggering compaction") 364 if err := db.Compact(nil, nil); err != nil { 365 log.Info("Compact err", "error", err) 366 return err 367 } 368 log.Info("Stats after compaction") 369 showLeveldbStats(db) 370 return nil 371 } 372 373 // dbGet shows the value of a given database key 374 func dbGet(ctx *cli.Context) error { 375 if ctx.NArg() != 1 { 376 return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage) 377 } 378 stack, _ := makeConfigNode(ctx) 379 defer stack.Close() 380 381 db := utils.MakeChainDatabase(ctx, stack, true) 382 defer db.Close() 383 384 key, err := parseHexOrString(ctx.Args().Get(0)) 385 if err != nil { 386 log.Info("Could not decode the key", "error", err) 387 return err 388 } 389 390 data, err := db.Get(key) 391 if err != nil { 392 log.Info("Get operation failed", "key", fmt.Sprintf("0x%#x", key), "error", err) 393 return err 394 } 395 fmt.Printf("key %#x: %#x\n", key, data) 396 return nil 397 } 398 399 // dbDelete deletes a key from the database 400 func dbDelete(ctx *cli.Context) error { 401 if ctx.NArg() != 1 { 402 return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage) 403 } 404 stack, _ := makeConfigNode(ctx) 405 defer stack.Close() 406 407 db := utils.MakeChainDatabase(ctx, stack, false) 408 defer db.Close() 409 410 key, err := parseHexOrString(ctx.Args().Get(0)) 411 if err != nil { 412 log.Info("Could not decode the key", "error", err) 413 return err 414 } 415 data, err := db.Get(key) 416 if err == nil { 417 fmt.Printf("Previous value: %#x\n", data) 418 } 419 if err = db.Delete(key); err != nil { 420 log.Info("Delete operation returned an error", "key", fmt.Sprintf("0x%#x", key), "error", err) 421 return err 422 } 423 return nil 424 } 425 426 // dbPut overwrite a value in the database 427 func dbPut(ctx *cli.Context) error { 428 if ctx.NArg() != 2 { 429 return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage) 430 } 431 stack, _ := makeConfigNode(ctx) 432 defer stack.Close() 433 434 db := utils.MakeChainDatabase(ctx, stack, false) 435 defer db.Close() 436 437 var ( 438 key []byte 439 value []byte 440 data []byte 441 err error 442 ) 443 key, err = parseHexOrString(ctx.Args().Get(0)) 444 if err != nil { 445 log.Info("Could not decode the key", "error", err) 446 return err 447 } 448 value, err = hexutil.Decode(ctx.Args().Get(1)) 449 if err != nil { 450 log.Info("Could not decode the value", "error", err) 451 return err 452 } 453 data, err = db.Get(key) 454 if err == nil { 455 fmt.Printf("Previous value: %#x\n", data) 456 } 457 return db.Put(key, value) 458 } 459 460 // dbDumpTrie shows the key-value slots of a given storage trie 461 func dbDumpTrie(ctx *cli.Context) error { 462 if ctx.NArg() < 1 { 463 return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage) 464 } 465 stack, _ := makeConfigNode(ctx) 466 defer stack.Close() 467 468 db := utils.MakeChainDatabase(ctx, stack, true) 469 defer db.Close() 470 var ( 471 root []byte 472 start []byte 473 max = int64(-1) 474 err error 475 ) 476 if root, err = hexutil.Decode(ctx.Args().Get(0)); err != nil { 477 log.Info("Could not decode the root", "error", err) 478 return err 479 } 480 stRoot := common.BytesToHash(root) 481 if ctx.NArg() >= 2 { 482 if start, err = hexutil.Decode(ctx.Args().Get(1)); err != nil { 483 log.Info("Could not decode the seek position", "error", err) 484 return err 485 } 486 } 487 if ctx.NArg() >= 3 { 488 if max, err = strconv.ParseInt(ctx.Args().Get(2), 10, 64); err != nil { 489 log.Info("Could not decode the max count", "error", err) 490 return err 491 } 492 } 493 theTrie, err := trie.New(stRoot, trie.NewDatabase(db)) 494 if err != nil { 495 return err 496 } 497 var count int64 498 it := trie.NewIterator(theTrie.NodeIterator(start)) 499 for it.Next() { 500 if max > 0 && count == max { 501 fmt.Printf("Exiting after %d values\n", count) 502 break 503 } 504 fmt.Printf(" %d. key %#x: %#x\n", count, it.Key, it.Value) 505 count++ 506 } 507 return it.Err 508 } 509 510 func freezerInspect(ctx *cli.Context) error { 511 var ( 512 start, end int64 513 disableSnappy bool 514 err error 515 ) 516 if ctx.NArg() < 3 { 517 return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage) 518 } 519 kind := ctx.Args().Get(0) 520 if noSnap, ok := rawdb.FreezerNoSnappy[kind]; !ok { 521 var options []string 522 for opt := range rawdb.FreezerNoSnappy { 523 options = append(options, opt) 524 } 525 sort.Strings(options) 526 return fmt.Errorf("Could read freezer-type '%v'. Available options: %v", kind, options) 527 } else { 528 disableSnappy = noSnap 529 } 530 if start, err = strconv.ParseInt(ctx.Args().Get(1), 10, 64); err != nil { 531 log.Info("Could read start-param", "error", err) 532 return err 533 } 534 if end, err = strconv.ParseInt(ctx.Args().Get(2), 10, 64); err != nil { 535 log.Info("Could read count param", "error", err) 536 return err 537 } 538 stack, _ := makeConfigNode(ctx) 539 defer stack.Close() 540 path := filepath.Join(stack.ResolvePath("chaindata"), "ancient") 541 log.Info("Opening freezer", "location", path, "name", kind) 542 if f, err := rawdb.NewFreezerTable(path, kind, disableSnappy); err != nil { 543 return err 544 } else { 545 f.DumpIndex(start, end) 546 } 547 return nil 548 } 549 550 // ParseHexOrString tries to hexdecode b, but if the prefix is missing, it instead just returns the raw bytes 551 func parseHexOrString(str string) ([]byte, error) { 552 b, err := hexutil.Decode(str) 553 if errors.Is(err, hexutil.ErrMissingPrefix) { 554 return []byte(str), nil 555 } 556 return b, err 557 } 558 559 func importLDBdata(ctx *cli.Context) error { 560 start := 0 561 switch ctx.NArg() { 562 case 1: 563 break 564 case 2: 565 s, err := strconv.Atoi(ctx.Args().Get(1)) 566 if err != nil { 567 return fmt.Errorf("second arg must be an integer: %v", err) 568 } 569 start = s 570 default: 571 return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage) 572 } 573 var ( 574 fName = ctx.Args().Get(0) 575 stack, _ = makeConfigNode(ctx) 576 interrupt = make(chan os.Signal, 1) 577 stop = make(chan struct{}) 578 ) 579 defer stack.Close() 580 signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM) 581 defer signal.Stop(interrupt) 582 defer close(interrupt) 583 go func() { 584 if _, ok := <-interrupt; ok { 585 log.Info("Interrupted during ldb import, stopping at next batch") 586 } 587 close(stop) 588 }() 589 db := utils.MakeChainDatabase(ctx, stack, false) 590 return utils.ImportLDBData(db, fName, int64(start), stop) 591 } 592 593 type preimageIterator struct { 594 iter ethdb.Iterator 595 } 596 597 func (iter *preimageIterator) Next() (byte, []byte, []byte, bool) { 598 for iter.iter.Next() { 599 key := iter.iter.Key() 600 if bytes.HasPrefix(key, rawdb.PreimagePrefix) && len(key) == (len(rawdb.PreimagePrefix)+common.HashLength) { 601 return utils.OpBatchAdd, key, iter.iter.Value(), true 602 } 603 } 604 return 0, nil, nil, false 605 } 606 607 func (iter *preimageIterator) Release() { 608 iter.iter.Release() 609 } 610 611 type snapshotIterator struct { 612 init bool 613 account ethdb.Iterator 614 storage ethdb.Iterator 615 } 616 617 func (iter *snapshotIterator) Next() (byte, []byte, []byte, bool) { 618 if !iter.init { 619 iter.init = true 620 return utils.OpBatchDel, rawdb.SnapshotRootKey, nil, true 621 } 622 for iter.account.Next() { 623 key := iter.account.Key() 624 if bytes.HasPrefix(key, rawdb.SnapshotAccountPrefix) && len(key) == (len(rawdb.SnapshotAccountPrefix)+common.HashLength) { 625 return utils.OpBatchAdd, key, iter.account.Value(), true 626 } 627 } 628 for iter.storage.Next() { 629 key := iter.storage.Key() 630 if bytes.HasPrefix(key, rawdb.SnapshotStoragePrefix) && len(key) == (len(rawdb.SnapshotStoragePrefix)+2*common.HashLength) { 631 return utils.OpBatchAdd, key, iter.storage.Value(), true 632 } 633 } 634 return 0, nil, nil, false 635 } 636 637 func (iter *snapshotIterator) Release() { 638 iter.account.Release() 639 iter.storage.Release() 640 } 641 642 // chainExporters defines the export scheme for all exportable chain data. 643 var chainExporters = map[string]func(db ethdb.Database) utils.ChainDataIterator{ 644 "preimage": func(db ethdb.Database) utils.ChainDataIterator { 645 iter := db.NewIterator(rawdb.PreimagePrefix, nil) 646 return &preimageIterator{iter: iter} 647 }, 648 "snapshot": func(db ethdb.Database) utils.ChainDataIterator { 649 account := db.NewIterator(rawdb.SnapshotAccountPrefix, nil) 650 storage := db.NewIterator(rawdb.SnapshotStoragePrefix, nil) 651 return &snapshotIterator{account: account, storage: storage} 652 }, 653 } 654 655 func exportChaindata(ctx *cli.Context) error { 656 if ctx.NArg() < 2 { 657 return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage) 658 } 659 // Parse the required chain data type, make sure it's supported. 660 kind := ctx.Args().Get(0) 661 kind = strings.ToLower(strings.Trim(kind, " ")) 662 exporter, ok := chainExporters[kind] 663 if !ok { 664 var kinds []string 665 for kind := range chainExporters { 666 kinds = append(kinds, kind) 667 } 668 return fmt.Errorf("invalid data type %s, supported types: %s", kind, strings.Join(kinds, ", ")) 669 } 670 var ( 671 stack, _ = makeConfigNode(ctx) 672 interrupt = make(chan os.Signal, 1) 673 stop = make(chan struct{}) 674 ) 675 defer stack.Close() 676 signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM) 677 defer signal.Stop(interrupt) 678 defer close(interrupt) 679 go func() { 680 if _, ok := <-interrupt; ok { 681 log.Info("Interrupted during db export, stopping at next batch") 682 } 683 close(stop) 684 }() 685 db := utils.MakeChainDatabase(ctx, stack, true) 686 return utils.ExportChaindata(ctx.Args().Get(1), kind, exporter(db), stop) 687 }