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