github.com/palisadeinc/bor@v0.0.0-20230615125219-ab7196213d15/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/ethereum/go-ethereum/cmd/utils" 33 "github.com/ethereum/go-ethereum/common" 34 "github.com/ethereum/go-ethereum/common/hexutil" 35 "github.com/ethereum/go-ethereum/console/prompt" 36 "github.com/ethereum/go-ethereum/core/rawdb" 37 "github.com/ethereum/go-ethereum/core/state/snapshot" 38 "github.com/ethereum/go-ethereum/core/types" 39 "github.com/ethereum/go-ethereum/ethdb" 40 "github.com/ethereum/go-ethereum/log" 41 "github.com/ethereum/go-ethereum/trie" 42 "github.com/olekukonko/tablewriter" 43 "gopkg.in/urfave/cli.v1" 44 ) 45 46 var ( 47 removedbCommand = cli.Command{ 48 Action: utils.MigrateFlags(removeDB), 49 Name: "removedb", 50 Usage: "Remove blockchain and state databases", 51 ArgsUsage: "", 52 Flags: []cli.Flag{ 53 utils.DataDirFlag, 54 }, 55 Category: "DATABASE COMMANDS", 56 Description: ` 57 Remove blockchain and state databases`, 58 } 59 dbCommand = cli.Command{ 60 Name: "db", 61 Usage: "Low level database operations", 62 ArgsUsage: "", 63 Category: "DATABASE COMMANDS", 64 Subcommands: []cli.Command{ 65 dbInspectCmd, 66 dbStatCmd, 67 dbCompactCmd, 68 dbGetCmd, 69 dbDeleteCmd, 70 dbPutCmd, 71 dbGetSlotsCmd, 72 dbDumpFreezerIndex, 73 dbImportCmd, 74 dbExportCmd, 75 dbMetadataCmd, 76 dbMigrateFreezerCmd, 77 }, 78 } 79 dbInspectCmd = cli.Command{ 80 Action: utils.MigrateFlags(inspect), 81 Name: "inspect", 82 ArgsUsage: "<prefix> <start>", 83 Flags: []cli.Flag{ 84 utils.DataDirFlag, 85 utils.AncientFlag, 86 utils.SyncModeFlag, 87 utils.MainnetFlag, 88 utils.RopstenFlag, 89 utils.SepoliaFlag, 90 utils.RinkebyFlag, 91 utils.GoerliFlag, 92 utils.MumbaiFlag, 93 utils.BorMainnetFlag, 94 }, 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 dbStatCmd = cli.Command{ 99 Action: utils.MigrateFlags(dbStats), 100 Name: "stats", 101 Usage: "Print leveldb statistics", 102 Flags: []cli.Flag{ 103 utils.DataDirFlag, 104 utils.SyncModeFlag, 105 utils.MainnetFlag, 106 utils.RopstenFlag, 107 utils.SepoliaFlag, 108 utils.RinkebyFlag, 109 utils.GoerliFlag, 110 utils.MumbaiFlag, 111 utils.BorMainnetFlag, 112 }, 113 } 114 dbCompactCmd = cli.Command{ 115 Action: utils.MigrateFlags(dbCompact), 116 Name: "compact", 117 Usage: "Compact leveldb database. WARNING: May take a very long time", 118 Flags: []cli.Flag{ 119 utils.DataDirFlag, 120 utils.SyncModeFlag, 121 utils.MainnetFlag, 122 utils.RopstenFlag, 123 utils.SepoliaFlag, 124 utils.RinkebyFlag, 125 utils.GoerliFlag, 126 utils.MumbaiFlag, 127 utils.BorMainnetFlag, 128 utils.CacheFlag, 129 utils.CacheDatabaseFlag, 130 }, 131 Description: `This command performs a database compaction. 132 WARNING: This operation may take a very long time to finish, and may cause database 133 corruption if it is aborted during execution'!`, 134 } 135 dbGetCmd = cli.Command{ 136 Action: utils.MigrateFlags(dbGet), 137 Name: "get", 138 Usage: "Show the value of a database key", 139 ArgsUsage: "<hex-encoded key>", 140 Flags: []cli.Flag{ 141 utils.DataDirFlag, 142 utils.SyncModeFlag, 143 utils.MainnetFlag, 144 utils.RopstenFlag, 145 utils.SepoliaFlag, 146 utils.RinkebyFlag, 147 utils.GoerliFlag, 148 utils.MumbaiFlag, 149 utils.BorMainnetFlag, 150 }, 151 Description: "This command looks up the specified database key from the database.", 152 } 153 dbDeleteCmd = cli.Command{ 154 Action: utils.MigrateFlags(dbDelete), 155 Name: "delete", 156 Usage: "Delete a database key (WARNING: may corrupt your database)", 157 ArgsUsage: "<hex-encoded key>", 158 Flags: []cli.Flag{ 159 utils.DataDirFlag, 160 utils.SyncModeFlag, 161 utils.MainnetFlag, 162 utils.RopstenFlag, 163 utils.SepoliaFlag, 164 utils.RinkebyFlag, 165 utils.GoerliFlag, 166 utils.MumbaiFlag, 167 utils.BorMainnetFlag, 168 }, 169 Description: `This command deletes the specified database key from the database. 170 WARNING: This is a low-level operation which may cause database corruption!`, 171 } 172 dbPutCmd = cli.Command{ 173 Action: utils.MigrateFlags(dbPut), 174 Name: "put", 175 Usage: "Set the value of a database key (WARNING: may corrupt your database)", 176 ArgsUsage: "<hex-encoded key> <hex-encoded value>", 177 Flags: []cli.Flag{ 178 utils.DataDirFlag, 179 utils.SyncModeFlag, 180 utils.MainnetFlag, 181 utils.RopstenFlag, 182 utils.SepoliaFlag, 183 utils.RinkebyFlag, 184 utils.GoerliFlag, 185 utils.MumbaiFlag, 186 utils.BorMainnetFlag, 187 }, 188 Description: `This command sets a given database key to the given value. 189 WARNING: This is a low-level operation which may cause database corruption!`, 190 } 191 dbGetSlotsCmd = cli.Command{ 192 Action: utils.MigrateFlags(dbDumpTrie), 193 Name: "dumptrie", 194 Usage: "Show the storage key/values of a given storage trie", 195 ArgsUsage: "<hex-encoded storage trie root> <hex-encoded start (optional)> <int max elements (optional)>", 196 Flags: []cli.Flag{ 197 utils.DataDirFlag, 198 utils.SyncModeFlag, 199 utils.MainnetFlag, 200 utils.RopstenFlag, 201 utils.SepoliaFlag, 202 utils.RinkebyFlag, 203 utils.GoerliFlag, 204 utils.MumbaiFlag, 205 utils.BorMainnetFlag, 206 }, 207 Description: "This command looks up the specified database key from the database.", 208 } 209 dbDumpFreezerIndex = cli.Command{ 210 Action: utils.MigrateFlags(freezerInspect), 211 Name: "freezer-index", 212 Usage: "Dump out the index of a given freezer type", 213 ArgsUsage: "<type> <start (int)> <end (int)>", 214 Flags: []cli.Flag{ 215 utils.DataDirFlag, 216 utils.SyncModeFlag, 217 utils.MainnetFlag, 218 utils.RopstenFlag, 219 utils.SepoliaFlag, 220 utils.RinkebyFlag, 221 utils.GoerliFlag, 222 utils.MumbaiFlag, 223 utils.BorMainnetFlag, 224 }, 225 Description: "This command displays information about the freezer index.", 226 } 227 dbImportCmd = cli.Command{ 228 Action: utils.MigrateFlags(importLDBdata), 229 Name: "import", 230 Usage: "Imports leveldb-data from an exported RLP dump.", 231 ArgsUsage: "<dumpfile> <start (optional)", 232 Flags: []cli.Flag{ 233 utils.DataDirFlag, 234 utils.SyncModeFlag, 235 utils.MainnetFlag, 236 utils.RopstenFlag, 237 utils.RinkebyFlag, 238 utils.GoerliFlag, 239 }, 240 Description: "The import command imports the specific chain data from an RLP encoded stream.", 241 } 242 dbExportCmd = cli.Command{ 243 Action: utils.MigrateFlags(exportChaindata), 244 Name: "export", 245 Usage: "Exports the chain data into an RLP dump. If the <dumpfile> has .gz suffix, gzip compression will be used.", 246 ArgsUsage: "<type> <dumpfile>", 247 Flags: []cli.Flag{ 248 utils.DataDirFlag, 249 utils.SyncModeFlag, 250 utils.MainnetFlag, 251 utils.RopstenFlag, 252 utils.RinkebyFlag, 253 utils.GoerliFlag, 254 }, 255 Description: "Exports the specified chain data to an RLP encoded stream, optionally gzip-compressed.", 256 } 257 dbMetadataCmd = cli.Command{ 258 Action: utils.MigrateFlags(showMetaData), 259 Name: "metadata", 260 Usage: "Shows metadata about the chain status.", 261 Flags: []cli.Flag{ 262 utils.DataDirFlag, 263 utils.SyncModeFlag, 264 utils.MainnetFlag, 265 utils.RopstenFlag, 266 utils.SepoliaFlag, 267 utils.RinkebyFlag, 268 utils.GoerliFlag, 269 }, 270 Description: "Shows metadata about the chain status.", 271 } 272 dbMigrateFreezerCmd = cli.Command{ 273 Action: utils.MigrateFlags(freezerMigrate), 274 Name: "freezer-migrate", 275 Usage: "Migrate legacy parts of the freezer. (WARNING: may take a long time)", 276 ArgsUsage: "", 277 Flags: []cli.Flag{ 278 utils.DataDirFlag, 279 utils.SyncModeFlag, 280 utils.MainnetFlag, 281 utils.RopstenFlag, 282 utils.SepoliaFlag, 283 utils.RinkebyFlag, 284 utils.GoerliFlag, 285 }, 286 Description: `The freezer-migrate command checks your database for receipts in a legacy format and updates those. 287 WARNING: please back-up the receipt files in your ancients before running this command.`, 288 } 289 ) 290 291 func removeDB(ctx *cli.Context) error { 292 stack, config := makeConfigNode(ctx) 293 294 // Remove the full node state database 295 path := stack.ResolvePath("chaindata") 296 if common.FileExist(path) { 297 confirmAndRemoveDB(path, "full node state database") 298 } else { 299 log.Info("Full node state database missing", "path", path) 300 } 301 // Remove the full node ancient database 302 path = config.Eth.DatabaseFreezer 303 switch { 304 case path == "": 305 path = filepath.Join(stack.ResolvePath("chaindata"), "ancient") 306 case !filepath.IsAbs(path): 307 path = config.Node.ResolvePath(path) 308 } 309 if common.FileExist(path) { 310 confirmAndRemoveDB(path, "full node ancient database") 311 } else { 312 log.Info("Full node ancient database missing", "path", path) 313 } 314 // Remove the light node database 315 path = stack.ResolvePath("lightchaindata") 316 if common.FileExist(path) { 317 confirmAndRemoveDB(path, "light node database") 318 } else { 319 log.Info("Light node database missing", "path", path) 320 } 321 return nil 322 } 323 324 // confirmAndRemoveDB prompts the user for a last confirmation and removes the 325 // folder if accepted. 326 func confirmAndRemoveDB(database string, kind string) { 327 confirm, err := prompt.Stdin.PromptConfirm(fmt.Sprintf("Remove %s (%s)?", kind, database)) 328 switch { 329 case err != nil: 330 utils.Fatalf("%v", err) 331 case !confirm: 332 log.Info("Database deletion skipped", "path", database) 333 default: 334 start := time.Now() 335 filepath.Walk(database, func(path string, info os.FileInfo, err error) error { 336 // If we're at the top level folder, recurse into 337 if path == database { 338 return nil 339 } 340 // Delete all the files, but not subfolders 341 if !info.IsDir() { 342 os.Remove(path) 343 return nil 344 } 345 return filepath.SkipDir 346 }) 347 log.Info("Database successfully deleted", "path", database, "elapsed", common.PrettyDuration(time.Since(start))) 348 } 349 } 350 351 func inspect(ctx *cli.Context) error { 352 var ( 353 prefix []byte 354 start []byte 355 ) 356 if ctx.NArg() > 2 { 357 return fmt.Errorf("Max 2 arguments: %v", ctx.Command.ArgsUsage) 358 } 359 if ctx.NArg() >= 1 { 360 if d, err := hexutil.Decode(ctx.Args().Get(0)); err != nil { 361 return fmt.Errorf("failed to hex-decode 'prefix': %v", err) 362 } else { 363 prefix = d 364 } 365 } 366 if ctx.NArg() >= 2 { 367 if d, err := hexutil.Decode(ctx.Args().Get(1)); err != nil { 368 return fmt.Errorf("failed to hex-decode 'start': %v", err) 369 } else { 370 start = d 371 } 372 } 373 stack, _ := makeConfigNode(ctx) 374 defer stack.Close() 375 376 db := utils.MakeChainDatabase(ctx, stack, true) 377 defer db.Close() 378 379 return rawdb.InspectDatabase(db, prefix, start) 380 } 381 382 func showLeveldbStats(db ethdb.Stater) { 383 if stats, err := db.Stat("leveldb.stats"); err != nil { 384 log.Warn("Failed to read database stats", "error", err) 385 } else { 386 fmt.Println(stats) 387 } 388 if ioStats, err := db.Stat("leveldb.iostats"); err != nil { 389 log.Warn("Failed to read database iostats", "error", err) 390 } else { 391 fmt.Println(ioStats) 392 } 393 } 394 395 func dbStats(ctx *cli.Context) error { 396 stack, _ := makeConfigNode(ctx) 397 defer stack.Close() 398 399 db := utils.MakeChainDatabase(ctx, stack, true) 400 defer db.Close() 401 402 showLeveldbStats(db) 403 return nil 404 } 405 406 func dbCompact(ctx *cli.Context) error { 407 stack, _ := makeConfigNode(ctx) 408 defer stack.Close() 409 410 db := utils.MakeChainDatabase(ctx, stack, false) 411 defer db.Close() 412 413 log.Info("Stats before compaction") 414 showLeveldbStats(db) 415 416 log.Info("Triggering compaction") 417 if err := db.Compact(nil, nil); err != nil { 418 log.Info("Compact err", "error", err) 419 return err 420 } 421 log.Info("Stats after compaction") 422 showLeveldbStats(db) 423 return nil 424 } 425 426 // dbGet shows the value of a given database key 427 func dbGet(ctx *cli.Context) error { 428 if ctx.NArg() != 1 { 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, true) 435 defer db.Close() 436 437 key, err := parseHexOrString(ctx.Args().Get(0)) 438 if err != nil { 439 log.Info("Could not decode the key", "error", err) 440 return err 441 } 442 443 data, err := db.Get(key) 444 if err != nil { 445 log.Info("Get operation failed", "key", fmt.Sprintf("0x%#x", key), "error", err) 446 return err 447 } 448 fmt.Printf("key %#x: %#x\n", key, data) 449 return nil 450 } 451 452 // dbDelete deletes a key from the database 453 func dbDelete(ctx *cli.Context) error { 454 if ctx.NArg() != 1 { 455 return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage) 456 } 457 stack, _ := makeConfigNode(ctx) 458 defer stack.Close() 459 460 db := utils.MakeChainDatabase(ctx, stack, false) 461 defer db.Close() 462 463 key, err := parseHexOrString(ctx.Args().Get(0)) 464 if err != nil { 465 log.Info("Could not decode the key", "error", err) 466 return err 467 } 468 data, err := db.Get(key) 469 if err == nil { 470 fmt.Printf("Previous value: %#x\n", data) 471 } 472 if err = db.Delete(key); err != nil { 473 log.Info("Delete operation returned an error", "key", fmt.Sprintf("0x%#x", key), "error", err) 474 return err 475 } 476 return nil 477 } 478 479 // dbPut overwrite a value in the database 480 func dbPut(ctx *cli.Context) error { 481 if ctx.NArg() != 2 { 482 return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage) 483 } 484 stack, _ := makeConfigNode(ctx) 485 defer stack.Close() 486 487 db := utils.MakeChainDatabase(ctx, stack, false) 488 defer db.Close() 489 490 var ( 491 key []byte 492 value []byte 493 data []byte 494 err error 495 ) 496 key, err = parseHexOrString(ctx.Args().Get(0)) 497 if err != nil { 498 log.Info("Could not decode the key", "error", err) 499 return err 500 } 501 value, err = hexutil.Decode(ctx.Args().Get(1)) 502 if err != nil { 503 log.Info("Could not decode the value", "error", err) 504 return err 505 } 506 data, err = db.Get(key) 507 if err == nil { 508 fmt.Printf("Previous value: %#x\n", data) 509 } 510 return db.Put(key, value) 511 } 512 513 // dbDumpTrie shows the key-value slots of a given storage trie 514 func dbDumpTrie(ctx *cli.Context) error { 515 if ctx.NArg() < 1 { 516 return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage) 517 } 518 stack, _ := makeConfigNode(ctx) 519 defer stack.Close() 520 521 db := utils.MakeChainDatabase(ctx, stack, true) 522 defer db.Close() 523 var ( 524 root []byte 525 start []byte 526 max = int64(-1) 527 err error 528 ) 529 if root, err = hexutil.Decode(ctx.Args().Get(0)); err != nil { 530 log.Info("Could not decode the root", "error", err) 531 return err 532 } 533 stRoot := common.BytesToHash(root) 534 if ctx.NArg() >= 2 { 535 if start, err = hexutil.Decode(ctx.Args().Get(1)); err != nil { 536 log.Info("Could not decode the seek position", "error", err) 537 return err 538 } 539 } 540 if ctx.NArg() >= 3 { 541 if max, err = strconv.ParseInt(ctx.Args().Get(2), 10, 64); err != nil { 542 log.Info("Could not decode the max count", "error", err) 543 return err 544 } 545 } 546 theTrie, err := trie.New(stRoot, trie.NewDatabase(db)) 547 if err != nil { 548 return err 549 } 550 var count int64 551 it := trie.NewIterator(theTrie.NodeIterator(start)) 552 for it.Next() { 553 if max > 0 && count == max { 554 fmt.Printf("Exiting after %d values\n", count) 555 break 556 } 557 fmt.Printf(" %d. key %#x: %#x\n", count, it.Key, it.Value) 558 count++ 559 } 560 return it.Err 561 } 562 563 func freezerInspect(ctx *cli.Context) error { 564 var ( 565 start, end int64 566 disableSnappy bool 567 err error 568 ) 569 if ctx.NArg() < 3 { 570 return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage) 571 } 572 kind := ctx.Args().Get(0) 573 if noSnap, ok := rawdb.FreezerNoSnappy[kind]; !ok { 574 var options []string 575 for opt := range rawdb.FreezerNoSnappy { 576 options = append(options, opt) 577 } 578 sort.Strings(options) 579 return fmt.Errorf("Could read freezer-type '%v'. Available options: %v", kind, options) 580 } else { 581 disableSnappy = noSnap 582 } 583 if start, err = strconv.ParseInt(ctx.Args().Get(1), 10, 64); err != nil { 584 log.Info("Could read start-param", "error", err) 585 return err 586 } 587 if end, err = strconv.ParseInt(ctx.Args().Get(2), 10, 64); err != nil { 588 log.Info("Could read count param", "error", err) 589 return err 590 } 591 stack, _ := makeConfigNode(ctx) 592 defer stack.Close() 593 path := filepath.Join(stack.ResolvePath("chaindata"), "ancient") 594 log.Info("Opening freezer", "location", path, "name", kind) 595 if f, err := rawdb.NewFreezerTable(path, kind, disableSnappy, true); err != nil { 596 return err 597 } else { 598 f.DumpIndex(start, end) 599 } 600 return nil 601 } 602 603 // ParseHexOrString tries to hexdecode b, but if the prefix is missing, it instead just returns the raw bytes 604 func parseHexOrString(str string) ([]byte, error) { 605 b, err := hexutil.Decode(str) 606 if errors.Is(err, hexutil.ErrMissingPrefix) { 607 return []byte(str), nil 608 } 609 return b, err 610 } 611 612 func importLDBdata(ctx *cli.Context) error { 613 start := 0 614 switch ctx.NArg() { 615 case 1: 616 break 617 case 2: 618 s, err := strconv.Atoi(ctx.Args().Get(1)) 619 if err != nil { 620 return fmt.Errorf("second arg must be an integer: %v", err) 621 } 622 start = s 623 default: 624 return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage) 625 } 626 var ( 627 fName = ctx.Args().Get(0) 628 stack, _ = makeConfigNode(ctx) 629 interrupt = make(chan os.Signal, 1) 630 stop = make(chan struct{}) 631 ) 632 defer stack.Close() 633 signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM) 634 defer signal.Stop(interrupt) 635 defer close(interrupt) 636 go func() { 637 if _, ok := <-interrupt; ok { 638 log.Info("Interrupted during ldb import, stopping at next batch") 639 } 640 close(stop) 641 }() 642 db := utils.MakeChainDatabase(ctx, stack, false) 643 return utils.ImportLDBData(db, fName, int64(start), stop) 644 } 645 646 type preimageIterator struct { 647 iter ethdb.Iterator 648 } 649 650 func (iter *preimageIterator) Next() (byte, []byte, []byte, bool) { 651 for iter.iter.Next() { 652 key := iter.iter.Key() 653 if bytes.HasPrefix(key, rawdb.PreimagePrefix) && len(key) == (len(rawdb.PreimagePrefix)+common.HashLength) { 654 return utils.OpBatchAdd, key, iter.iter.Value(), true 655 } 656 } 657 return 0, nil, nil, false 658 } 659 660 func (iter *preimageIterator) Release() { 661 iter.iter.Release() 662 } 663 664 type snapshotIterator struct { 665 init bool 666 account ethdb.Iterator 667 storage ethdb.Iterator 668 } 669 670 func (iter *snapshotIterator) Next() (byte, []byte, []byte, bool) { 671 if !iter.init { 672 iter.init = true 673 return utils.OpBatchDel, rawdb.SnapshotRootKey, nil, true 674 } 675 for iter.account.Next() { 676 key := iter.account.Key() 677 if bytes.HasPrefix(key, rawdb.SnapshotAccountPrefix) && len(key) == (len(rawdb.SnapshotAccountPrefix)+common.HashLength) { 678 return utils.OpBatchAdd, key, iter.account.Value(), true 679 } 680 } 681 for iter.storage.Next() { 682 key := iter.storage.Key() 683 if bytes.HasPrefix(key, rawdb.SnapshotStoragePrefix) && len(key) == (len(rawdb.SnapshotStoragePrefix)+2*common.HashLength) { 684 return utils.OpBatchAdd, key, iter.storage.Value(), true 685 } 686 } 687 return 0, nil, nil, false 688 } 689 690 func (iter *snapshotIterator) Release() { 691 iter.account.Release() 692 iter.storage.Release() 693 } 694 695 // chainExporters defines the export scheme for all exportable chain data. 696 var chainExporters = map[string]func(db ethdb.Database) utils.ChainDataIterator{ 697 "preimage": func(db ethdb.Database) utils.ChainDataIterator { 698 iter := db.NewIterator(rawdb.PreimagePrefix, nil) 699 return &preimageIterator{iter: iter} 700 }, 701 "snapshot": func(db ethdb.Database) utils.ChainDataIterator { 702 account := db.NewIterator(rawdb.SnapshotAccountPrefix, nil) 703 storage := db.NewIterator(rawdb.SnapshotStoragePrefix, nil) 704 return &snapshotIterator{account: account, storage: storage} 705 }, 706 } 707 708 func exportChaindata(ctx *cli.Context) error { 709 if ctx.NArg() < 2 { 710 return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage) 711 } 712 // Parse the required chain data type, make sure it's supported. 713 kind := ctx.Args().Get(0) 714 kind = strings.ToLower(strings.Trim(kind, " ")) 715 exporter, ok := chainExporters[kind] 716 if !ok { 717 var kinds []string 718 for kind := range chainExporters { 719 kinds = append(kinds, kind) 720 } 721 return fmt.Errorf("invalid data type %s, supported types: %s", kind, strings.Join(kinds, ", ")) 722 } 723 var ( 724 stack, _ = makeConfigNode(ctx) 725 interrupt = make(chan os.Signal, 1) 726 stop = make(chan struct{}) 727 ) 728 defer stack.Close() 729 signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM) 730 defer signal.Stop(interrupt) 731 defer close(interrupt) 732 go func() { 733 if _, ok := <-interrupt; ok { 734 log.Info("Interrupted during db export, stopping at next batch") 735 } 736 close(stop) 737 }() 738 db := utils.MakeChainDatabase(ctx, stack, true) 739 return utils.ExportChaindata(ctx.Args().Get(1), kind, exporter(db), stop) 740 } 741 742 func showMetaData(ctx *cli.Context) error { 743 stack, _ := makeConfigNode(ctx) 744 defer stack.Close() 745 db := utils.MakeChainDatabase(ctx, stack, true) 746 ancients, err := db.Ancients() 747 if err != nil { 748 fmt.Fprintf(os.Stderr, "Error accessing ancients: %v", err) 749 } 750 pp := func(val *uint64) string { 751 if val == nil { 752 return "<nil>" 753 } 754 return fmt.Sprintf("%d (0x%x)", *val, *val) 755 } 756 data := [][]string{ 757 {"databaseVersion", pp(rawdb.ReadDatabaseVersion(db))}, 758 {"headBlockHash", fmt.Sprintf("%v", rawdb.ReadHeadBlockHash(db))}, 759 {"headFastBlockHash", fmt.Sprintf("%v", rawdb.ReadHeadFastBlockHash(db))}, 760 {"headHeaderHash", fmt.Sprintf("%v", rawdb.ReadHeadHeaderHash(db))}} 761 if b := rawdb.ReadHeadBlock(db); b != nil { 762 data = append(data, []string{"headBlock.Hash", fmt.Sprintf("%v", b.Hash())}) 763 data = append(data, []string{"headBlock.Root", fmt.Sprintf("%v", b.Root())}) 764 data = append(data, []string{"headBlock.Number", fmt.Sprintf("%d (0x%x)", b.Number(), b.Number())}) 765 } 766 if b := rawdb.ReadSkeletonSyncStatus(db); b != nil { 767 data = append(data, []string{"SkeletonSyncStatus", string(b)}) 768 } 769 if h := rawdb.ReadHeadHeader(db); h != nil { 770 data = append(data, []string{"headHeader.Hash", fmt.Sprintf("%v", h.Hash())}) 771 data = append(data, []string{"headHeader.Root", fmt.Sprintf("%v", h.Root)}) 772 data = append(data, []string{"headHeader.Number", fmt.Sprintf("%d (0x%x)", h.Number, h.Number)}) 773 } 774 data = append(data, [][]string{{"frozen", fmt.Sprintf("%d items", ancients)}, 775 {"lastPivotNumber", pp(rawdb.ReadLastPivotNumber(db))}, 776 {"len(snapshotSyncStatus)", fmt.Sprintf("%d bytes", len(rawdb.ReadSnapshotSyncStatus(db)))}, 777 {"snapshotGenerator", snapshot.ParseGeneratorStatus(rawdb.ReadSnapshotGenerator(db))}, 778 {"snapshotDisabled", fmt.Sprintf("%v", rawdb.ReadSnapshotDisabled(db))}, 779 {"snapshotJournal", fmt.Sprintf("%d bytes", len(rawdb.ReadSnapshotJournal(db)))}, 780 {"snapshotRecoveryNumber", pp(rawdb.ReadSnapshotRecoveryNumber(db))}, 781 {"snapshotRoot", fmt.Sprintf("%v", rawdb.ReadSnapshotRoot(db))}, 782 {"txIndexTail", pp(rawdb.ReadTxIndexTail(db))}, 783 {"fastTxLookupLimit", pp(rawdb.ReadFastTxLookupLimit(db))}, 784 }...) 785 table := tablewriter.NewWriter(os.Stdout) 786 table.SetHeader([]string{"Field", "Value"}) 787 table.AppendBulk(data) 788 table.Render() 789 return nil 790 } 791 792 func freezerMigrate(ctx *cli.Context) error { 793 stack, _ := makeConfigNode(ctx) 794 defer stack.Close() 795 796 db := utils.MakeChainDatabase(ctx, stack, false) 797 defer db.Close() 798 799 // Check first block for legacy receipt format 800 numAncients, err := db.Ancients() 801 if err != nil { 802 return err 803 } 804 if numAncients < 1 { 805 log.Info("No receipts in freezer to migrate") 806 return nil 807 } 808 809 isFirstLegacy, firstIdx, err := dbHasLegacyReceipts(db, 0) 810 if err != nil { 811 return err 812 } 813 if !isFirstLegacy { 814 log.Info("No legacy receipts to migrate") 815 return nil 816 } 817 818 log.Info("Starting migration", "ancients", numAncients, "firstLegacy", firstIdx) 819 start := time.Now() 820 if err := db.MigrateTable("receipts", types.ConvertLegacyStoredReceipts); err != nil { 821 return err 822 } 823 if err := db.Close(); err != nil { 824 return err 825 } 826 log.Info("Migration finished", "duration", time.Since(start)) 827 828 return nil 829 } 830 831 // dbHasLegacyReceipts checks freezer entries for legacy receipts. It stops at the first 832 // non-empty receipt and checks its format. The index of this first non-empty element is 833 // the second return parameter. 834 func dbHasLegacyReceipts(db ethdb.Database, firstIdx uint64) (bool, uint64, error) { 835 // Check first block for legacy receipt format 836 numAncients, err := db.Ancients() 837 if err != nil { 838 return false, 0, err 839 } 840 if numAncients < 1 { 841 return false, 0, nil 842 } 843 if firstIdx >= numAncients { 844 return false, firstIdx, nil 845 } 846 var ( 847 legacy bool 848 blob []byte 849 emptyRLPList = []byte{192} 850 ) 851 // Find first block with non-empty receipt, only if 852 // the index is not already provided. 853 if firstIdx == 0 { 854 for i := uint64(0); i < numAncients; i++ { 855 blob, err = db.Ancient("receipts", i) 856 if err != nil { 857 return false, 0, err 858 } 859 if len(blob) == 0 { 860 continue 861 } 862 if !bytes.Equal(blob, emptyRLPList) { 863 firstIdx = i 864 break 865 } 866 } 867 } 868 // Is first non-empty receipt legacy? 869 first, err := db.Ancient("receipts", firstIdx) 870 if err != nil { 871 return false, 0, err 872 } 873 legacy, err = types.IsLegacyStoredReceipts(first) 874 return legacy, firstIdx, err 875 }