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