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