github.com/ethereum/go-ethereum@v1.14.4-0.20240516095835-473ee8fc07a3/cmd/geth/dbcmd.go (about) 1 // Copyright 2021 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 "fmt" 22 "os" 23 "os/signal" 24 "path/filepath" 25 "strconv" 26 "strings" 27 "syscall" 28 "time" 29 30 "github.com/ethereum/go-ethereum/cmd/utils" 31 "github.com/ethereum/go-ethereum/common" 32 "github.com/ethereum/go-ethereum/common/hexutil" 33 "github.com/ethereum/go-ethereum/console/prompt" 34 "github.com/ethereum/go-ethereum/core/rawdb" 35 "github.com/ethereum/go-ethereum/core/state/snapshot" 36 "github.com/ethereum/go-ethereum/core/types" 37 "github.com/ethereum/go-ethereum/crypto" 38 "github.com/ethereum/go-ethereum/ethdb" 39 "github.com/ethereum/go-ethereum/internal/flags" 40 "github.com/ethereum/go-ethereum/log" 41 "github.com/ethereum/go-ethereum/rlp" 42 "github.com/ethereum/go-ethereum/trie" 43 "github.com/ethereum/go-ethereum/triedb" 44 "github.com/olekukonko/tablewriter" 45 "github.com/urfave/cli/v2" 46 ) 47 48 var ( 49 removeStateDataFlag = &cli.BoolFlag{ 50 Name: "remove.state", 51 Usage: "If set, selects the state data for removal", 52 } 53 removeChainDataFlag = &cli.BoolFlag{ 54 Name: "remove.chain", 55 Usage: "If set, selects the state data for removal", 56 } 57 58 removedbCommand = &cli.Command{ 59 Action: removeDB, 60 Name: "removedb", 61 Usage: "Remove blockchain and state databases", 62 ArgsUsage: "", 63 Flags: flags.Merge(utils.DatabaseFlags, 64 []cli.Flag{removeStateDataFlag, removeChainDataFlag}), 65 Description: ` 66 Remove blockchain and state databases`, 67 } 68 dbCommand = &cli.Command{ 69 Name: "db", 70 Usage: "Low level database operations", 71 ArgsUsage: "", 72 Subcommands: []*cli.Command{ 73 dbInspectCmd, 74 dbStatCmd, 75 dbCompactCmd, 76 dbGetCmd, 77 dbDeleteCmd, 78 dbPutCmd, 79 dbGetSlotsCmd, 80 dbDumpFreezerIndex, 81 dbImportCmd, 82 dbExportCmd, 83 dbMetadataCmd, 84 dbCheckStateContentCmd, 85 dbInspectHistoryCmd, 86 }, 87 } 88 dbInspectCmd = &cli.Command{ 89 Action: inspect, 90 Name: "inspect", 91 ArgsUsage: "<prefix> <start>", 92 Flags: flags.Merge([]cli.Flag{ 93 utils.SyncModeFlag, 94 }, utils.NetworkFlags, utils.DatabaseFlags), 95 Usage: "Inspect the storage size for each type of data in the database", 96 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.`, 97 } 98 dbCheckStateContentCmd = &cli.Command{ 99 Action: checkStateContent, 100 Name: "check-state-content", 101 ArgsUsage: "<start (optional)>", 102 Flags: flags.Merge(utils.NetworkFlags, utils.DatabaseFlags), 103 Usage: "Verify that state data is cryptographically correct", 104 Description: `This command iterates the entire database for 32-byte keys, looking for rlp-encoded trie nodes. 105 For each trie node encountered, it checks that the key corresponds to the keccak256(value). If this is not true, this indicates 106 a data corruption.`, 107 } 108 dbStatCmd = &cli.Command{ 109 Action: dbStats, 110 Name: "stats", 111 Usage: "Print leveldb statistics", 112 Flags: flags.Merge([]cli.Flag{ 113 utils.SyncModeFlag, 114 }, utils.NetworkFlags, utils.DatabaseFlags), 115 } 116 dbCompactCmd = &cli.Command{ 117 Action: dbCompact, 118 Name: "compact", 119 Usage: "Compact leveldb database. WARNING: May take a very long time", 120 Flags: flags.Merge([]cli.Flag{ 121 utils.SyncModeFlag, 122 utils.CacheFlag, 123 utils.CacheDatabaseFlag, 124 }, utils.NetworkFlags, utils.DatabaseFlags), 125 Description: `This command performs a database compaction. 126 WARNING: This operation may take a very long time to finish, and may cause database 127 corruption if it is aborted during execution'!`, 128 } 129 dbGetCmd = &cli.Command{ 130 Action: dbGet, 131 Name: "get", 132 Usage: "Show the value of a database key", 133 ArgsUsage: "<hex-encoded key>", 134 Flags: flags.Merge([]cli.Flag{ 135 utils.SyncModeFlag, 136 }, utils.NetworkFlags, utils.DatabaseFlags), 137 Description: "This command looks up the specified database key from the database.", 138 } 139 dbDeleteCmd = &cli.Command{ 140 Action: dbDelete, 141 Name: "delete", 142 Usage: "Delete a database key (WARNING: may corrupt your database)", 143 ArgsUsage: "<hex-encoded key>", 144 Flags: flags.Merge([]cli.Flag{ 145 utils.SyncModeFlag, 146 }, utils.NetworkFlags, utils.DatabaseFlags), 147 Description: `This command deletes the specified database key from the database. 148 WARNING: This is a low-level operation which may cause database corruption!`, 149 } 150 dbPutCmd = &cli.Command{ 151 Action: dbPut, 152 Name: "put", 153 Usage: "Set the value of a database key (WARNING: may corrupt your database)", 154 ArgsUsage: "<hex-encoded key> <hex-encoded value>", 155 Flags: flags.Merge([]cli.Flag{ 156 utils.SyncModeFlag, 157 }, utils.NetworkFlags, utils.DatabaseFlags), 158 Description: `This command sets a given database key to the given value. 159 WARNING: This is a low-level operation which may cause database corruption!`, 160 } 161 dbGetSlotsCmd = &cli.Command{ 162 Action: dbDumpTrie, 163 Name: "dumptrie", 164 Usage: "Show the storage key/values of a given storage trie", 165 ArgsUsage: "<hex-encoded state root> <hex-encoded account hash> <hex-encoded storage trie root> <hex-encoded start (optional)> <int max elements (optional)>", 166 Flags: flags.Merge([]cli.Flag{ 167 utils.SyncModeFlag, 168 }, utils.NetworkFlags, utils.DatabaseFlags), 169 Description: "This command looks up the specified database key from the database.", 170 } 171 dbDumpFreezerIndex = &cli.Command{ 172 Action: freezerInspect, 173 Name: "freezer-index", 174 Usage: "Dump out the index of a specific freezer table", 175 ArgsUsage: "<freezer-type> <table-type> <start (int)> <end (int)>", 176 Flags: flags.Merge([]cli.Flag{ 177 utils.SyncModeFlag, 178 }, utils.NetworkFlags, utils.DatabaseFlags), 179 Description: "This command displays information about the freezer index.", 180 } 181 dbImportCmd = &cli.Command{ 182 Action: importLDBdata, 183 Name: "import", 184 Usage: "Imports leveldb-data from an exported RLP dump.", 185 ArgsUsage: "<dumpfile> <start (optional)", 186 Flags: flags.Merge([]cli.Flag{ 187 utils.SyncModeFlag, 188 }, utils.NetworkFlags, utils.DatabaseFlags), 189 Description: "The import command imports the specific chain data from an RLP encoded stream.", 190 } 191 dbExportCmd = &cli.Command{ 192 Action: exportChaindata, 193 Name: "export", 194 Usage: "Exports the chain data into an RLP dump. If the <dumpfile> has .gz suffix, gzip compression will be used.", 195 ArgsUsage: "<type> <dumpfile>", 196 Flags: flags.Merge([]cli.Flag{ 197 utils.SyncModeFlag, 198 }, utils.NetworkFlags, utils.DatabaseFlags), 199 Description: "Exports the specified chain data to an RLP encoded stream, optionally gzip-compressed.", 200 } 201 dbMetadataCmd = &cli.Command{ 202 Action: showMetaData, 203 Name: "metadata", 204 Usage: "Shows metadata about the chain status.", 205 Flags: flags.Merge([]cli.Flag{ 206 utils.SyncModeFlag, 207 }, utils.NetworkFlags, utils.DatabaseFlags), 208 Description: "Shows metadata about the chain status.", 209 } 210 dbInspectHistoryCmd = &cli.Command{ 211 Action: inspectHistory, 212 Name: "inspect-history", 213 Usage: "Inspect the state history within block range", 214 ArgsUsage: "<address> [OPTIONAL <storage-slot>]", 215 Flags: flags.Merge([]cli.Flag{ 216 utils.SyncModeFlag, 217 &cli.Uint64Flag{ 218 Name: "start", 219 Usage: "block number of the range start, zero means earliest history", 220 }, 221 &cli.Uint64Flag{ 222 Name: "end", 223 Usage: "block number of the range end(included), zero means latest history", 224 }, 225 &cli.BoolFlag{ 226 Name: "raw", 227 Usage: "display the decoded raw state value (otherwise shows rlp-encoded value)", 228 }, 229 }, utils.NetworkFlags, utils.DatabaseFlags), 230 Description: "This command queries the history of the account or storage slot within the specified block range", 231 } 232 ) 233 234 func removeDB(ctx *cli.Context) error { 235 stack, config := makeConfigNode(ctx) 236 237 // Resolve folder paths. 238 var ( 239 rootDir = stack.ResolvePath("chaindata") 240 ancientDir = config.Eth.DatabaseFreezer 241 ) 242 switch { 243 case ancientDir == "": 244 ancientDir = filepath.Join(stack.ResolvePath("chaindata"), "ancient") 245 case !filepath.IsAbs(ancientDir): 246 ancientDir = config.Node.ResolvePath(ancientDir) 247 } 248 // Delete state data 249 statePaths := []string{ 250 rootDir, 251 filepath.Join(ancientDir, rawdb.StateFreezerName), 252 } 253 confirmAndRemoveDB(statePaths, "state data", ctx, removeStateDataFlag.Name) 254 255 // Delete ancient chain 256 chainPaths := []string{filepath.Join( 257 ancientDir, 258 rawdb.ChainFreezerName, 259 )} 260 confirmAndRemoveDB(chainPaths, "ancient chain", ctx, removeChainDataFlag.Name) 261 return nil 262 } 263 264 // removeFolder deletes all files (not folders) inside the directory 'dir' (but 265 // not files in subfolders). 266 func removeFolder(dir string) { 267 filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { 268 // If we're at the top level folder, recurse into 269 if path == dir { 270 return nil 271 } 272 // Delete all the files, but not subfolders 273 if !info.IsDir() { 274 os.Remove(path) 275 return nil 276 } 277 return filepath.SkipDir 278 }) 279 } 280 281 // confirmAndRemoveDB prompts the user for a last confirmation and removes the 282 // list of folders if accepted. 283 func confirmAndRemoveDB(paths []string, kind string, ctx *cli.Context, removeFlagName string) { 284 var ( 285 confirm bool 286 err error 287 ) 288 msg := fmt.Sprintf("Location(s) of '%s': \n", kind) 289 for _, path := range paths { 290 msg += fmt.Sprintf("\t- %s\n", path) 291 } 292 fmt.Println(msg) 293 if ctx.IsSet(removeFlagName) { 294 confirm = ctx.Bool(removeFlagName) 295 if confirm { 296 fmt.Printf("Remove '%s'? [y/n] y\n", kind) 297 } else { 298 fmt.Printf("Remove '%s'? [y/n] n\n", kind) 299 } 300 } else { 301 confirm, err = prompt.Stdin.PromptConfirm(fmt.Sprintf("Remove '%s'?", kind)) 302 } 303 switch { 304 case err != nil: 305 utils.Fatalf("%v", err) 306 case !confirm: 307 log.Info("Database deletion skipped", "kind", kind, "paths", paths) 308 default: 309 var ( 310 deleted []string 311 start = time.Now() 312 ) 313 for _, path := range paths { 314 if common.FileExist(path) { 315 removeFolder(path) 316 deleted = append(deleted, path) 317 } else { 318 log.Info("Folder is not existent", "path", path) 319 } 320 } 321 log.Info("Database successfully deleted", "kind", kind, "paths", deleted, "elapsed", common.PrettyDuration(time.Since(start))) 322 } 323 } 324 325 func inspect(ctx *cli.Context) error { 326 var ( 327 prefix []byte 328 start []byte 329 ) 330 if ctx.NArg() > 2 { 331 return fmt.Errorf("max 2 arguments: %v", ctx.Command.ArgsUsage) 332 } 333 if ctx.NArg() >= 1 { 334 if d, err := hexutil.Decode(ctx.Args().Get(0)); err != nil { 335 return fmt.Errorf("failed to hex-decode 'prefix': %v", err) 336 } else { 337 prefix = d 338 } 339 } 340 if ctx.NArg() >= 2 { 341 if d, err := hexutil.Decode(ctx.Args().Get(1)); err != nil { 342 return fmt.Errorf("failed to hex-decode 'start': %v", err) 343 } else { 344 start = d 345 } 346 } 347 stack, _ := makeConfigNode(ctx) 348 defer stack.Close() 349 350 db := utils.MakeChainDatabase(ctx, stack, true) 351 defer db.Close() 352 353 return rawdb.InspectDatabase(db, prefix, start) 354 } 355 356 func checkStateContent(ctx *cli.Context) error { 357 var ( 358 prefix []byte 359 start []byte 360 ) 361 if ctx.NArg() > 1 { 362 return fmt.Errorf("max 1 argument: %v", ctx.Command.ArgsUsage) 363 } 364 if ctx.NArg() > 0 { 365 if d, err := hexutil.Decode(ctx.Args().First()); err != nil { 366 return fmt.Errorf("failed to hex-decode 'start': %v", err) 367 } else { 368 start = d 369 } 370 } 371 stack, _ := makeConfigNode(ctx) 372 defer stack.Close() 373 374 db := utils.MakeChainDatabase(ctx, stack, true) 375 defer db.Close() 376 var ( 377 it = rawdb.NewKeyLengthIterator(db.NewIterator(prefix, start), 32) 378 hasher = crypto.NewKeccakState() 379 got = make([]byte, 32) 380 errs int 381 count int 382 startTime = time.Now() 383 lastLog = time.Now() 384 ) 385 for it.Next() { 386 count++ 387 k := it.Key() 388 v := it.Value() 389 hasher.Reset() 390 hasher.Write(v) 391 hasher.Read(got) 392 if !bytes.Equal(k, got) { 393 errs++ 394 fmt.Printf("Error at %#x\n", k) 395 fmt.Printf(" Hash: %#x\n", got) 396 fmt.Printf(" Data: %#x\n", v) 397 } 398 if time.Since(lastLog) > 8*time.Second { 399 log.Info("Iterating the database", "at", fmt.Sprintf("%#x", k), "elapsed", common.PrettyDuration(time.Since(startTime))) 400 lastLog = time.Now() 401 } 402 } 403 if err := it.Error(); err != nil { 404 return err 405 } 406 log.Info("Iterated the state content", "errors", errs, "items", count) 407 return nil 408 } 409 410 func showLeveldbStats(db ethdb.KeyValueStater) { 411 if stats, err := db.Stat("leveldb.stats"); err != nil { 412 log.Warn("Failed to read database stats", "error", err) 413 } else { 414 fmt.Println(stats) 415 } 416 if ioStats, err := db.Stat("leveldb.iostats"); err != nil { 417 log.Warn("Failed to read database iostats", "error", err) 418 } else { 419 fmt.Println(ioStats) 420 } 421 } 422 423 func dbStats(ctx *cli.Context) error { 424 stack, _ := makeConfigNode(ctx) 425 defer stack.Close() 426 427 db := utils.MakeChainDatabase(ctx, stack, true) 428 defer db.Close() 429 430 showLeveldbStats(db) 431 return nil 432 } 433 434 func dbCompact(ctx *cli.Context) error { 435 stack, _ := makeConfigNode(ctx) 436 defer stack.Close() 437 438 db := utils.MakeChainDatabase(ctx, stack, false) 439 defer db.Close() 440 441 log.Info("Stats before compaction") 442 showLeveldbStats(db) 443 444 log.Info("Triggering compaction") 445 if err := db.Compact(nil, nil); err != nil { 446 log.Info("Compact err", "error", err) 447 return err 448 } 449 log.Info("Stats after compaction") 450 showLeveldbStats(db) 451 return nil 452 } 453 454 // dbGet shows the value of a given database key 455 func dbGet(ctx *cli.Context) error { 456 if ctx.NArg() != 1 { 457 return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage) 458 } 459 stack, _ := makeConfigNode(ctx) 460 defer stack.Close() 461 462 db := utils.MakeChainDatabase(ctx, stack, true) 463 defer db.Close() 464 465 key, err := common.ParseHexOrString(ctx.Args().Get(0)) 466 if err != nil { 467 log.Info("Could not decode the key", "error", err) 468 return err 469 } 470 471 data, err := db.Get(key) 472 if err != nil { 473 log.Info("Get operation failed", "key", fmt.Sprintf("%#x", key), "error", err) 474 return err 475 } 476 fmt.Printf("key %#x: %#x\n", key, data) 477 return nil 478 } 479 480 // dbDelete deletes a key from the database 481 func dbDelete(ctx *cli.Context) error { 482 if ctx.NArg() != 1 { 483 return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage) 484 } 485 stack, _ := makeConfigNode(ctx) 486 defer stack.Close() 487 488 db := utils.MakeChainDatabase(ctx, stack, false) 489 defer db.Close() 490 491 key, err := common.ParseHexOrString(ctx.Args().Get(0)) 492 if err != nil { 493 log.Info("Could not decode the key", "error", err) 494 return err 495 } 496 data, err := db.Get(key) 497 if err == nil { 498 fmt.Printf("Previous value: %#x\n", data) 499 } 500 if err = db.Delete(key); err != nil { 501 log.Info("Delete operation returned an error", "key", fmt.Sprintf("%#x", key), "error", err) 502 return err 503 } 504 return nil 505 } 506 507 // dbPut overwrite a value in the database 508 func dbPut(ctx *cli.Context) error { 509 if ctx.NArg() != 2 { 510 return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage) 511 } 512 stack, _ := makeConfigNode(ctx) 513 defer stack.Close() 514 515 db := utils.MakeChainDatabase(ctx, stack, false) 516 defer db.Close() 517 518 var ( 519 key []byte 520 value []byte 521 data []byte 522 err error 523 ) 524 key, err = common.ParseHexOrString(ctx.Args().Get(0)) 525 if err != nil { 526 log.Info("Could not decode the key", "error", err) 527 return err 528 } 529 value, err = hexutil.Decode(ctx.Args().Get(1)) 530 if err != nil { 531 log.Info("Could not decode the value", "error", err) 532 return err 533 } 534 data, err = db.Get(key) 535 if err == nil { 536 fmt.Printf("Previous value: %#x\n", data) 537 } 538 return db.Put(key, value) 539 } 540 541 // dbDumpTrie shows the key-value slots of a given storage trie 542 func dbDumpTrie(ctx *cli.Context) error { 543 if ctx.NArg() < 3 { 544 return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage) 545 } 546 stack, _ := makeConfigNode(ctx) 547 defer stack.Close() 548 549 db := utils.MakeChainDatabase(ctx, stack, true) 550 defer db.Close() 551 552 triedb := utils.MakeTrieDatabase(ctx, db, false, true, false) 553 defer triedb.Close() 554 555 var ( 556 state []byte 557 storage []byte 558 account []byte 559 start []byte 560 max = int64(-1) 561 err error 562 ) 563 if state, err = hexutil.Decode(ctx.Args().Get(0)); err != nil { 564 log.Info("Could not decode the state root", "error", err) 565 return err 566 } 567 if account, err = hexutil.Decode(ctx.Args().Get(1)); err != nil { 568 log.Info("Could not decode the account hash", "error", err) 569 return err 570 } 571 if storage, err = hexutil.Decode(ctx.Args().Get(2)); err != nil { 572 log.Info("Could not decode the storage trie root", "error", err) 573 return err 574 } 575 if ctx.NArg() > 3 { 576 if start, err = hexutil.Decode(ctx.Args().Get(3)); err != nil { 577 log.Info("Could not decode the seek position", "error", err) 578 return err 579 } 580 } 581 if ctx.NArg() > 4 { 582 if max, err = strconv.ParseInt(ctx.Args().Get(4), 10, 64); err != nil { 583 log.Info("Could not decode the max count", "error", err) 584 return err 585 } 586 } 587 id := trie.StorageTrieID(common.BytesToHash(state), common.BytesToHash(account), common.BytesToHash(storage)) 588 theTrie, err := trie.New(id, triedb) 589 if err != nil { 590 return err 591 } 592 trieIt, err := theTrie.NodeIterator(start) 593 if err != nil { 594 return err 595 } 596 var count int64 597 it := trie.NewIterator(trieIt) 598 for it.Next() { 599 if max > 0 && count == max { 600 fmt.Printf("Exiting after %d values\n", count) 601 break 602 } 603 fmt.Printf(" %d. key %#x: %#x\n", count, it.Key, it.Value) 604 count++ 605 } 606 return it.Err 607 } 608 609 func freezerInspect(ctx *cli.Context) error { 610 if ctx.NArg() < 4 { 611 return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage) 612 } 613 var ( 614 freezer = ctx.Args().Get(0) 615 table = ctx.Args().Get(1) 616 ) 617 start, err := strconv.ParseInt(ctx.Args().Get(2), 10, 64) 618 if err != nil { 619 log.Info("Could not read start-param", "err", err) 620 return err 621 } 622 end, err := strconv.ParseInt(ctx.Args().Get(3), 10, 64) 623 if err != nil { 624 log.Info("Could not read count param", "err", err) 625 return err 626 } 627 stack, _ := makeConfigNode(ctx) 628 ancient := stack.ResolveAncient("chaindata", ctx.String(utils.AncientFlag.Name)) 629 stack.Close() 630 return rawdb.InspectFreezerTable(ancient, freezer, table, start, end) 631 } 632 633 func importLDBdata(ctx *cli.Context) error { 634 start := 0 635 switch ctx.NArg() { 636 case 1: 637 break 638 case 2: 639 s, err := strconv.Atoi(ctx.Args().Get(1)) 640 if err != nil { 641 return fmt.Errorf("second arg must be an integer: %v", err) 642 } 643 start = s 644 default: 645 return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage) 646 } 647 var ( 648 fName = ctx.Args().Get(0) 649 stack, _ = makeConfigNode(ctx) 650 interrupt = make(chan os.Signal, 1) 651 stop = make(chan struct{}) 652 ) 653 defer stack.Close() 654 signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM) 655 defer signal.Stop(interrupt) 656 defer close(interrupt) 657 go func() { 658 if _, ok := <-interrupt; ok { 659 log.Info("Interrupted during ldb import, stopping at next batch") 660 } 661 close(stop) 662 }() 663 db := utils.MakeChainDatabase(ctx, stack, false) 664 defer db.Close() 665 return utils.ImportLDBData(db, fName, int64(start), stop) 666 } 667 668 type preimageIterator struct { 669 iter ethdb.Iterator 670 } 671 672 func (iter *preimageIterator) Next() (byte, []byte, []byte, bool) { 673 for iter.iter.Next() { 674 key := iter.iter.Key() 675 if bytes.HasPrefix(key, rawdb.PreimagePrefix) && len(key) == (len(rawdb.PreimagePrefix)+common.HashLength) { 676 return utils.OpBatchAdd, key, iter.iter.Value(), true 677 } 678 } 679 return 0, nil, nil, false 680 } 681 682 func (iter *preimageIterator) Release() { 683 iter.iter.Release() 684 } 685 686 type snapshotIterator struct { 687 init bool 688 account ethdb.Iterator 689 storage ethdb.Iterator 690 } 691 692 func (iter *snapshotIterator) Next() (byte, []byte, []byte, bool) { 693 if !iter.init { 694 iter.init = true 695 return utils.OpBatchDel, rawdb.SnapshotRootKey, nil, true 696 } 697 for iter.account.Next() { 698 key := iter.account.Key() 699 if bytes.HasPrefix(key, rawdb.SnapshotAccountPrefix) && len(key) == (len(rawdb.SnapshotAccountPrefix)+common.HashLength) { 700 return utils.OpBatchAdd, key, iter.account.Value(), true 701 } 702 } 703 for iter.storage.Next() { 704 key := iter.storage.Key() 705 if bytes.HasPrefix(key, rawdb.SnapshotStoragePrefix) && len(key) == (len(rawdb.SnapshotStoragePrefix)+2*common.HashLength) { 706 return utils.OpBatchAdd, key, iter.storage.Value(), true 707 } 708 } 709 return 0, nil, nil, false 710 } 711 712 func (iter *snapshotIterator) Release() { 713 iter.account.Release() 714 iter.storage.Release() 715 } 716 717 // chainExporters defines the export scheme for all exportable chain data. 718 var chainExporters = map[string]func(db ethdb.Database) utils.ChainDataIterator{ 719 "preimage": func(db ethdb.Database) utils.ChainDataIterator { 720 iter := db.NewIterator(rawdb.PreimagePrefix, nil) 721 return &preimageIterator{iter: iter} 722 }, 723 "snapshot": func(db ethdb.Database) utils.ChainDataIterator { 724 account := db.NewIterator(rawdb.SnapshotAccountPrefix, nil) 725 storage := db.NewIterator(rawdb.SnapshotStoragePrefix, nil) 726 return &snapshotIterator{account: account, storage: storage} 727 }, 728 } 729 730 func exportChaindata(ctx *cli.Context) error { 731 if ctx.NArg() < 2 { 732 return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage) 733 } 734 // Parse the required chain data type, make sure it's supported. 735 kind := ctx.Args().Get(0) 736 kind = strings.ToLower(strings.Trim(kind, " ")) 737 exporter, ok := chainExporters[kind] 738 if !ok { 739 var kinds []string 740 for kind := range chainExporters { 741 kinds = append(kinds, kind) 742 } 743 return fmt.Errorf("invalid data type %s, supported types: %s", kind, strings.Join(kinds, ", ")) 744 } 745 var ( 746 stack, _ = makeConfigNode(ctx) 747 interrupt = make(chan os.Signal, 1) 748 stop = make(chan struct{}) 749 ) 750 defer stack.Close() 751 signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM) 752 defer signal.Stop(interrupt) 753 defer close(interrupt) 754 go func() { 755 if _, ok := <-interrupt; ok { 756 log.Info("Interrupted during db export, stopping at next batch") 757 } 758 close(stop) 759 }() 760 db := utils.MakeChainDatabase(ctx, stack, true) 761 defer db.Close() 762 return utils.ExportChaindata(ctx.Args().Get(1), kind, exporter(db), stop) 763 } 764 765 func showMetaData(ctx *cli.Context) error { 766 stack, _ := makeConfigNode(ctx) 767 defer stack.Close() 768 db := utils.MakeChainDatabase(ctx, stack, true) 769 defer db.Close() 770 771 ancients, err := db.Ancients() 772 if err != nil { 773 fmt.Fprintf(os.Stderr, "Error accessing ancients: %v", err) 774 } 775 data := rawdb.ReadChainMetadata(db) 776 data = append(data, []string{"frozen", fmt.Sprintf("%d items", ancients)}) 777 data = append(data, []string{"snapshotGenerator", snapshot.ParseGeneratorStatus(rawdb.ReadSnapshotGenerator(db))}) 778 if b := rawdb.ReadHeadBlock(db); b != nil { 779 data = append(data, []string{"headBlock.Hash", fmt.Sprintf("%v", b.Hash())}) 780 data = append(data, []string{"headBlock.Root", fmt.Sprintf("%v", b.Root())}) 781 data = append(data, []string{"headBlock.Number", fmt.Sprintf("%d (%#x)", b.Number(), b.Number())}) 782 } 783 if h := rawdb.ReadHeadHeader(db); h != nil { 784 data = append(data, []string{"headHeader.Hash", fmt.Sprintf("%v", h.Hash())}) 785 data = append(data, []string{"headHeader.Root", fmt.Sprintf("%v", h.Root)}) 786 data = append(data, []string{"headHeader.Number", fmt.Sprintf("%d (%#x)", h.Number, h.Number)}) 787 } 788 table := tablewriter.NewWriter(os.Stdout) 789 table.SetHeader([]string{"Field", "Value"}) 790 table.AppendBulk(data) 791 table.Render() 792 return nil 793 } 794 795 func inspectAccount(db *triedb.Database, start uint64, end uint64, address common.Address, raw bool) error { 796 stats, err := db.AccountHistory(address, start, end) 797 if err != nil { 798 return err 799 } 800 fmt.Printf("Account history:\n\taddress: %s\n\tblockrange: [#%d-#%d]\n", address.Hex(), stats.Start, stats.End) 801 802 from := stats.Start 803 for i := 0; i < len(stats.Blocks); i++ { 804 var content string 805 if len(stats.Origins[i]) == 0 { 806 content = "<empty>" 807 } else { 808 if !raw { 809 content = fmt.Sprintf("%#x", stats.Origins[i]) 810 } else { 811 account := new(types.SlimAccount) 812 if err := rlp.DecodeBytes(stats.Origins[i], account); err != nil { 813 panic(err) 814 } 815 code := "<nil>" 816 if len(account.CodeHash) > 0 { 817 code = fmt.Sprintf("%#x", account.CodeHash) 818 } 819 root := "<nil>" 820 if len(account.Root) > 0 { 821 root = fmt.Sprintf("%#x", account.Root) 822 } 823 content = fmt.Sprintf("nonce: %d, balance: %d, codeHash: %s, root: %s", account.Nonce, account.Balance, code, root) 824 } 825 } 826 fmt.Printf("#%d - #%d: %s\n", from, stats.Blocks[i], content) 827 from = stats.Blocks[i] 828 } 829 return nil 830 } 831 832 func inspectStorage(db *triedb.Database, start uint64, end uint64, address common.Address, slot common.Hash, raw bool) error { 833 // The hash of storage slot key is utilized in the history 834 // rather than the raw slot key, make the conversion. 835 slotHash := crypto.Keccak256Hash(slot.Bytes()) 836 stats, err := db.StorageHistory(address, slotHash, start, end) 837 if err != nil { 838 return err 839 } 840 fmt.Printf("Storage history:\n\taddress: %s\n\tslot: %s\n\tblockrange: [#%d-#%d]\n", address.Hex(), slot.Hex(), stats.Start, stats.End) 841 842 from := stats.Start 843 for i := 0; i < len(stats.Blocks); i++ { 844 var content string 845 if len(stats.Origins[i]) == 0 { 846 content = "<empty>" 847 } else { 848 if !raw { 849 content = fmt.Sprintf("%#x", stats.Origins[i]) 850 } else { 851 _, data, _, err := rlp.Split(stats.Origins[i]) 852 if err != nil { 853 fmt.Printf("Failed to decode storage slot, %v", err) 854 return err 855 } 856 content = fmt.Sprintf("%#x", data) 857 } 858 } 859 fmt.Printf("#%d - #%d: %s\n", from, stats.Blocks[i], content) 860 from = stats.Blocks[i] 861 } 862 return nil 863 } 864 865 func inspectHistory(ctx *cli.Context) error { 866 if ctx.NArg() == 0 || ctx.NArg() > 2 { 867 return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage) 868 } 869 var ( 870 address common.Address 871 slot common.Hash 872 ) 873 if err := address.UnmarshalText([]byte(ctx.Args().Get(0))); err != nil { 874 return err 875 } 876 if ctx.NArg() > 1 { 877 if err := slot.UnmarshalText([]byte(ctx.Args().Get(1))); err != nil { 878 return err 879 } 880 } 881 // Load the databases. 882 stack, _ := makeConfigNode(ctx) 883 defer stack.Close() 884 885 db := utils.MakeChainDatabase(ctx, stack, true) 886 defer db.Close() 887 888 triedb := utils.MakeTrieDatabase(ctx, db, false, false, false) 889 defer triedb.Close() 890 891 var ( 892 err error 893 start uint64 // the id of first history object to query 894 end uint64 // the id (included) of last history object to query 895 ) 896 // State histories are identified by state ID rather than block number. 897 // To address this, load the corresponding block header and perform the 898 // conversion by this function. 899 blockToID := func(blockNumber uint64) (uint64, error) { 900 header := rawdb.ReadHeader(db, rawdb.ReadCanonicalHash(db, blockNumber), blockNumber) 901 if header == nil { 902 return 0, fmt.Errorf("block #%d is not existent", blockNumber) 903 } 904 id := rawdb.ReadStateID(db, header.Root) 905 if id == nil { 906 first, last, err := triedb.HistoryRange() 907 if err == nil { 908 return 0, fmt.Errorf("history of block #%d is not existent, available history range: [#%d-#%d]", blockNumber, first, last) 909 } 910 return 0, fmt.Errorf("history of block #%d is not existent", blockNumber) 911 } 912 return *id, nil 913 } 914 // Parse the starting block number for inspection. 915 startNumber := ctx.Uint64("start") 916 if startNumber != 0 { 917 start, err = blockToID(startNumber) 918 if err != nil { 919 return err 920 } 921 } 922 // Parse the ending block number for inspection. 923 endBlock := ctx.Uint64("end") 924 if endBlock != 0 { 925 end, err = blockToID(endBlock) 926 if err != nil { 927 return err 928 } 929 } 930 // Inspect the state history. 931 if slot == (common.Hash{}) { 932 return inspectAccount(triedb, start, end, address, ctx.Bool("raw")) 933 } 934 return inspectStorage(triedb, start, end, address, slot, ctx.Bool("raw")) 935 }