@@ -20,6 +20,7 @@ import (
2020 "encoding/json"
2121 "fmt"
2222 "os"
23+ "path/filepath"
2324 "runtime"
2425 "strconv"
2526 "sync/atomic"
@@ -167,6 +168,22 @@ Remove blockchain and state databases`,
167168The arguments are interpreted as block numbers or hashes.
168169Use "ethereum dump 0" to dump the genesis block.` ,
169170 }
171+ inspectCommand = cli.Command {
172+ Action : utils .MigrateFlags (inspect ),
173+ Name : "inspect" ,
174+ Usage : "Inspect the storage size for each type of data in the database" ,
175+ ArgsUsage : " " ,
176+ Flags : []cli.Flag {
177+ utils .DataDirFlag ,
178+ utils .AncientFlag ,
179+ utils .CacheFlag ,
180+ utils .TestnetFlag ,
181+ utils .RinkebyFlag ,
182+ utils .GoerliFlag ,
183+ utils .SyncModeFlag ,
184+ },
185+ Category : "BLOCKCHAIN COMMANDS" ,
186+ }
170187)
171188
172189// initGenesis will initialise the given JSON format genesis file and writes it as
@@ -368,9 +385,12 @@ func exportPreimages(ctx *cli.Context) error {
368385
369386func copyDb (ctx * cli.Context ) error {
370387 // Ensure we have a source chain directory to copy
371- if len (ctx .Args ()) != 1 {
388+ if len (ctx .Args ()) < 1 {
372389 utils .Fatalf ("Source chaindata directory path argument missing" )
373390 }
391+ if len (ctx .Args ()) < 2 {
392+ utils .Fatalf ("Source ancient chain directory path argument missing" )
393+ }
374394 // Initialize a new chain for the running node to sync into
375395 stack := makeFullNode (ctx )
376396 defer stack .Close ()
@@ -385,7 +405,7 @@ func copyDb(ctx *cli.Context) error {
385405 dl := downloader .New (0 , chainDb , syncBloom , new (event.TypeMux ), chain , nil , nil )
386406
387407 // Create a source peer to satisfy downloader requests from
388- db , err := rawdb .NewLevelDBDatabase (ctx .Args ().First (), ctx .GlobalInt (utils .CacheFlag .Name )/ 2 , 256 , "" )
408+ db , err := rawdb .NewLevelDBDatabaseWithFreezer (ctx .Args ().First (), ctx .GlobalInt (utils .CacheFlag .Name )/ 2 , 256 , ctx . Args (). Get ( 1 ) , "" )
389409 if err != nil {
390410 return err
391411 }
@@ -420,34 +440,65 @@ func copyDb(ctx *cli.Context) error {
420440}
421441
422442func removeDB (ctx * cli.Context ) error {
423- stack , _ := makeConfigNode (ctx )
443+ stack , config := makeConfigNode (ctx )
424444
425- for _ , name := range []string {"chaindata" , "lightchaindata" } {
426- // Ensure the database exists in the first place
427- logger := log .New ("database" , name )
428-
429- dbdir := stack .ResolvePath (name )
430- if ! common .FileExist (dbdir ) {
431- logger .Info ("Database doesn't exist, skipping" , "path" , dbdir )
432- continue
433- }
434- // Confirm removal and execute
435- fmt .Println (dbdir )
436- confirm , err := console .Stdin .PromptConfirm ("Remove this database?" )
437- switch {
438- case err != nil :
439- utils .Fatalf ("%v" , err )
440- case ! confirm :
441- logger .Warn ("Database deletion aborted" )
442- default :
443- start := time .Now ()
444- os .RemoveAll (dbdir )
445- logger .Info ("Database successfully deleted" , "elapsed" , common .PrettyDuration (time .Since (start )))
446- }
445+ // Remove the full node state database
446+ path := stack .ResolvePath ("chaindata" )
447+ if common .FileExist (path ) {
448+ confirmAndRemoveDB (path , "full node state database" )
449+ } else {
450+ log .Info ("Full node state database missing" , "path" , path )
451+ }
452+ // Remove the full node ancient database
453+ path = config .Eth .DatabaseFreezer
454+ switch {
455+ case path == "" :
456+ path = filepath .Join (stack .ResolvePath ("chaindata" ), "ancient" )
457+ case ! filepath .IsAbs (path ):
458+ path = config .Node .ResolvePath (path )
459+ }
460+ if common .FileExist (path ) {
461+ confirmAndRemoveDB (path , "full node ancient database" )
462+ } else {
463+ log .Info ("Full node ancient database missing" , "path" , path )
464+ }
465+ // Remove the light node database
466+ path = stack .ResolvePath ("lightchaindata" )
467+ if common .FileExist (path ) {
468+ confirmAndRemoveDB (path , "light node database" )
469+ } else {
470+ log .Info ("Light node database missing" , "path" , path )
447471 }
448472 return nil
449473}
450474
475+ // confirmAndRemoveDB prompts the user for a last confirmation and removes the
476+ // folder if accepted.
477+ func confirmAndRemoveDB (database string , kind string ) {
478+ confirm , err := console .Stdin .PromptConfirm (fmt .Sprintf ("Remove %s (%s)?" , kind , database ))
479+ switch {
480+ case err != nil :
481+ utils .Fatalf ("%v" , err )
482+ case ! confirm :
483+ log .Info ("Database deletion skipped" , "path" , database )
484+ default :
485+ start := time .Now ()
486+ filepath .Walk (database , func (path string , info os.FileInfo , err error ) error {
487+ // If we're at the top level folder, recurse into
488+ if path == database {
489+ return nil
490+ }
491+ // Delete all the files, but not subfolders
492+ if ! info .IsDir () {
493+ os .Remove (path )
494+ return nil
495+ }
496+ return filepath .SkipDir
497+ })
498+ log .Info ("Database successfully deleted" , "path" , database , "elapsed" , common .PrettyDuration (time .Since (start )))
499+ }
500+ }
501+
451502func dump (ctx * cli.Context ) error {
452503 stack := makeFullNode (ctx )
453504 defer stack .Close ()
@@ -476,6 +527,16 @@ func dump(ctx *cli.Context) error {
476527 return nil
477528}
478529
530+ func inspect (ctx * cli.Context ) error {
531+ node , _ := makeConfigNode (ctx )
532+ defer node .Close ()
533+
534+ _ , chainDb := utils .MakeChain (ctx , node )
535+ defer chainDb .Close ()
536+
537+ return rawdb .InspectDatabase (chainDb )
538+ }
539+
479540// hashish returns true for strings that look like hashes.
480541func hashish (x string ) bool {
481542 _ , err := strconv .Atoi (x )
0 commit comments