github.com/mister-meeseeks/go-ethereum@v1.9.7/cmd/geth/chaincmd.go (about) 1 // Copyright 2015 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 "encoding/json" 21 "fmt" 22 "os" 23 "path/filepath" 24 "runtime" 25 "strconv" 26 "sync/atomic" 27 "time" 28 29 "github.com/ethereum/go-ethereum/cmd/utils" 30 "github.com/ethereum/go-ethereum/common" 31 "github.com/ethereum/go-ethereum/console" 32 "github.com/ethereum/go-ethereum/core" 33 "github.com/ethereum/go-ethereum/core/rawdb" 34 "github.com/ethereum/go-ethereum/core/state" 35 "github.com/ethereum/go-ethereum/core/types" 36 "github.com/ethereum/go-ethereum/eth/downloader" 37 "github.com/ethereum/go-ethereum/event" 38 "github.com/ethereum/go-ethereum/log" 39 "github.com/ethereum/go-ethereum/trie" 40 "gopkg.in/urfave/cli.v1" 41 ) 42 43 var ( 44 initCommand = cli.Command{ 45 Action: utils.MigrateFlags(initGenesis), 46 Name: "init", 47 Usage: "Bootstrap and initialize a new genesis block", 48 ArgsUsage: "<genesisPath>", 49 Flags: []cli.Flag{ 50 utils.DataDirFlag, 51 }, 52 Category: "BLOCKCHAIN COMMANDS", 53 Description: ` 54 The init command initializes a new genesis block and definition for the network. 55 This is a destructive action and changes the network in which you will be 56 participating. 57 58 It expects the genesis file as argument.`, 59 } 60 importCommand = cli.Command{ 61 Action: utils.MigrateFlags(importChain), 62 Name: "import", 63 Usage: "Import a blockchain file", 64 ArgsUsage: "<filename> (<filename 2> ... <filename N>) ", 65 Flags: []cli.Flag{ 66 utils.DataDirFlag, 67 utils.CacheFlag, 68 utils.SyncModeFlag, 69 utils.GCModeFlag, 70 utils.CacheDatabaseFlag, 71 utils.CacheGCFlag, 72 }, 73 Category: "BLOCKCHAIN COMMANDS", 74 Description: ` 75 The import command imports blocks from an RLP-encoded form. The form can be one file 76 with several RLP-encoded blocks, or several files can be used. 77 78 If only one file is used, import error will result in failure. If several files are used, 79 processing will proceed even if an individual RLP-file import failure occurs.`, 80 } 81 exportCommand = cli.Command{ 82 Action: utils.MigrateFlags(exportChain), 83 Name: "export", 84 Usage: "Export blockchain into file", 85 ArgsUsage: "<filename> [<blockNumFirst> <blockNumLast>]", 86 Flags: []cli.Flag{ 87 utils.DataDirFlag, 88 utils.CacheFlag, 89 utils.SyncModeFlag, 90 }, 91 Category: "BLOCKCHAIN COMMANDS", 92 Description: ` 93 Requires a first argument of the file to write to. 94 Optional second and third arguments control the first and 95 last block to write. In this mode, the file will be appended 96 if already existing. If the file ends with .gz, the output will 97 be gzipped.`, 98 } 99 importPreimagesCommand = cli.Command{ 100 Action: utils.MigrateFlags(importPreimages), 101 Name: "import-preimages", 102 Usage: "Import the preimage database from an RLP stream", 103 ArgsUsage: "<datafile>", 104 Flags: []cli.Flag{ 105 utils.DataDirFlag, 106 utils.CacheFlag, 107 utils.SyncModeFlag, 108 }, 109 Category: "BLOCKCHAIN COMMANDS", 110 Description: ` 111 The import-preimages command imports hash preimages from an RLP encoded stream.`, 112 } 113 exportPreimagesCommand = cli.Command{ 114 Action: utils.MigrateFlags(exportPreimages), 115 Name: "export-preimages", 116 Usage: "Export the preimage database into an RLP stream", 117 ArgsUsage: "<dumpfile>", 118 Flags: []cli.Flag{ 119 utils.DataDirFlag, 120 utils.CacheFlag, 121 utils.SyncModeFlag, 122 }, 123 Category: "BLOCKCHAIN COMMANDS", 124 Description: ` 125 The export-preimages command export hash preimages to an RLP encoded stream`, 126 } 127 copydbCommand = cli.Command{ 128 Action: utils.MigrateFlags(copyDb), 129 Name: "copydb", 130 Usage: "Create a local chain from a target chaindata folder", 131 ArgsUsage: "<sourceChaindataDir>", 132 Flags: []cli.Flag{ 133 utils.DataDirFlag, 134 utils.CacheFlag, 135 utils.SyncModeFlag, 136 utils.FakePoWFlag, 137 utils.TestnetFlag, 138 utils.RinkebyFlag, 139 }, 140 Category: "BLOCKCHAIN COMMANDS", 141 Description: ` 142 The first argument must be the directory containing the blockchain to download from`, 143 } 144 removedbCommand = cli.Command{ 145 Action: utils.MigrateFlags(removeDB), 146 Name: "removedb", 147 Usage: "Remove blockchain and state databases", 148 ArgsUsage: " ", 149 Flags: []cli.Flag{ 150 utils.DataDirFlag, 151 }, 152 Category: "BLOCKCHAIN COMMANDS", 153 Description: ` 154 Remove blockchain and state databases`, 155 } 156 dumpCommand = cli.Command{ 157 Action: utils.MigrateFlags(dump), 158 Name: "dump", 159 Usage: "Dump a specific block from storage", 160 ArgsUsage: "[<blockHash> | <blockNum>]...", 161 Flags: []cli.Flag{ 162 utils.DataDirFlag, 163 utils.CacheFlag, 164 utils.SyncModeFlag, 165 utils.IterativeOutputFlag, 166 utils.ExcludeCodeFlag, 167 utils.ExcludeStorageFlag, 168 utils.IncludeIncompletesFlag, 169 }, 170 Category: "BLOCKCHAIN COMMANDS", 171 Description: ` 172 The arguments are interpreted as block numbers or hashes. 173 Use "ethereum dump 0" to dump the genesis block.`, 174 } 175 inspectCommand = cli.Command{ 176 Action: utils.MigrateFlags(inspect), 177 Name: "inspect", 178 Usage: "Inspect the storage size for each type of data in the database", 179 ArgsUsage: " ", 180 Flags: []cli.Flag{ 181 utils.DataDirFlag, 182 utils.AncientFlag, 183 utils.CacheFlag, 184 utils.TestnetFlag, 185 utils.RinkebyFlag, 186 utils.GoerliFlag, 187 utils.SyncModeFlag, 188 }, 189 Category: "BLOCKCHAIN COMMANDS", 190 } 191 ) 192 193 // initGenesis will initialise the given JSON format genesis file and writes it as 194 // the zero'd block (i.e. genesis) or will fail hard if it can't succeed. 195 func initGenesis(ctx *cli.Context) error { 196 // Make sure we have a valid genesis JSON 197 genesisPath := ctx.Args().First() 198 if len(genesisPath) == 0 { 199 utils.Fatalf("Must supply path to genesis JSON file") 200 } 201 file, err := os.Open(genesisPath) 202 if err != nil { 203 utils.Fatalf("Failed to read genesis file: %v", err) 204 } 205 defer file.Close() 206 207 genesis := new(core.Genesis) 208 if err := json.NewDecoder(file).Decode(genesis); err != nil { 209 utils.Fatalf("invalid genesis file: %v", err) 210 } 211 // Open an initialise both full and light databases 212 stack := makeFullNode(ctx) 213 defer stack.Close() 214 215 for _, name := range []string{"chaindata", "lightchaindata"} { 216 chaindb, err := stack.OpenDatabase(name, 0, 0, "") 217 if err != nil { 218 utils.Fatalf("Failed to open database: %v", err) 219 } 220 _, hash, err := core.SetupGenesisBlock(chaindb, genesis) 221 if err != nil { 222 utils.Fatalf("Failed to write genesis block: %v", err) 223 } 224 chaindb.Close() 225 log.Info("Successfully wrote genesis state", "database", name, "hash", hash) 226 } 227 return nil 228 } 229 230 func importChain(ctx *cli.Context) error { 231 if len(ctx.Args()) < 1 { 232 utils.Fatalf("This command requires an argument.") 233 } 234 stack := makeFullNode(ctx) 235 defer stack.Close() 236 237 chain, db := utils.MakeChain(ctx, stack) 238 defer db.Close() 239 240 // Start periodically gathering memory profiles 241 var peakMemAlloc, peakMemSys uint64 242 go func() { 243 stats := new(runtime.MemStats) 244 for { 245 runtime.ReadMemStats(stats) 246 if atomic.LoadUint64(&peakMemAlloc) < stats.Alloc { 247 atomic.StoreUint64(&peakMemAlloc, stats.Alloc) 248 } 249 if atomic.LoadUint64(&peakMemSys) < stats.Sys { 250 atomic.StoreUint64(&peakMemSys, stats.Sys) 251 } 252 time.Sleep(5 * time.Second) 253 } 254 }() 255 // Import the chain 256 start := time.Now() 257 258 if len(ctx.Args()) == 1 { 259 if err := utils.ImportChain(chain, ctx.Args().First()); err != nil { 260 log.Error("Import error", "err", err) 261 } 262 } else { 263 for _, arg := range ctx.Args() { 264 if err := utils.ImportChain(chain, arg); err != nil { 265 log.Error("Import error", "file", arg, "err", err) 266 } 267 } 268 } 269 chain.Stop() 270 fmt.Printf("Import done in %v.\n\n", time.Since(start)) 271 272 // Output pre-compaction stats mostly to see the import trashing 273 stats, err := db.Stat("leveldb.stats") 274 if err != nil { 275 utils.Fatalf("Failed to read database stats: %v", err) 276 } 277 fmt.Println(stats) 278 279 ioStats, err := db.Stat("leveldb.iostats") 280 if err != nil { 281 utils.Fatalf("Failed to read database iostats: %v", err) 282 } 283 fmt.Println(ioStats) 284 285 // Print the memory statistics used by the importing 286 mem := new(runtime.MemStats) 287 runtime.ReadMemStats(mem) 288 289 fmt.Printf("Object memory: %.3f MB current, %.3f MB peak\n", float64(mem.Alloc)/1024/1024, float64(atomic.LoadUint64(&peakMemAlloc))/1024/1024) 290 fmt.Printf("System memory: %.3f MB current, %.3f MB peak\n", float64(mem.Sys)/1024/1024, float64(atomic.LoadUint64(&peakMemSys))/1024/1024) 291 fmt.Printf("Allocations: %.3f million\n", float64(mem.Mallocs)/1000000) 292 fmt.Printf("GC pause: %v\n\n", time.Duration(mem.PauseTotalNs)) 293 294 if ctx.GlobalBool(utils.NoCompactionFlag.Name) { 295 return nil 296 } 297 298 // Compact the entire database to more accurately measure disk io and print the stats 299 start = time.Now() 300 fmt.Println("Compacting entire database...") 301 if err = db.Compact(nil, nil); err != nil { 302 utils.Fatalf("Compaction failed: %v", err) 303 } 304 fmt.Printf("Compaction done in %v.\n\n", time.Since(start)) 305 306 stats, err = db.Stat("leveldb.stats") 307 if err != nil { 308 utils.Fatalf("Failed to read database stats: %v", err) 309 } 310 fmt.Println(stats) 311 312 ioStats, err = db.Stat("leveldb.iostats") 313 if err != nil { 314 utils.Fatalf("Failed to read database iostats: %v", err) 315 } 316 fmt.Println(ioStats) 317 return nil 318 } 319 320 func exportChain(ctx *cli.Context) error { 321 if len(ctx.Args()) < 1 { 322 utils.Fatalf("This command requires an argument.") 323 } 324 stack := makeFullNode(ctx) 325 defer stack.Close() 326 327 chain, _ := utils.MakeChain(ctx, stack) 328 start := time.Now() 329 330 var err error 331 fp := ctx.Args().First() 332 if len(ctx.Args()) < 3 { 333 err = utils.ExportChain(chain, fp) 334 } else { 335 // This can be improved to allow for numbers larger than 9223372036854775807 336 first, ferr := strconv.ParseInt(ctx.Args().Get(1), 10, 64) 337 last, lerr := strconv.ParseInt(ctx.Args().Get(2), 10, 64) 338 if ferr != nil || lerr != nil { 339 utils.Fatalf("Export error in parsing parameters: block number not an integer\n") 340 } 341 if first < 0 || last < 0 { 342 utils.Fatalf("Export error: block number must be greater than 0\n") 343 } 344 err = utils.ExportAppendChain(chain, fp, uint64(first), uint64(last)) 345 } 346 347 if err != nil { 348 utils.Fatalf("Export error: %v\n", err) 349 } 350 fmt.Printf("Export done in %v\n", time.Since(start)) 351 return nil 352 } 353 354 // importPreimages imports preimage data from the specified file. 355 func importPreimages(ctx *cli.Context) error { 356 if len(ctx.Args()) < 1 { 357 utils.Fatalf("This command requires an argument.") 358 } 359 stack := makeFullNode(ctx) 360 defer stack.Close() 361 362 db := utils.MakeChainDatabase(ctx, stack) 363 start := time.Now() 364 365 if err := utils.ImportPreimages(db, ctx.Args().First()); err != nil { 366 utils.Fatalf("Import error: %v\n", err) 367 } 368 fmt.Printf("Import done in %v\n", time.Since(start)) 369 return nil 370 } 371 372 // exportPreimages dumps the preimage data to specified json file in streaming way. 373 func exportPreimages(ctx *cli.Context) error { 374 if len(ctx.Args()) < 1 { 375 utils.Fatalf("This command requires an argument.") 376 } 377 stack := makeFullNode(ctx) 378 defer stack.Close() 379 380 db := utils.MakeChainDatabase(ctx, stack) 381 start := time.Now() 382 383 if err := utils.ExportPreimages(db, ctx.Args().First()); err != nil { 384 utils.Fatalf("Export error: %v\n", err) 385 } 386 fmt.Printf("Export done in %v\n", time.Since(start)) 387 return nil 388 } 389 390 func copyDb(ctx *cli.Context) error { 391 // Ensure we have a source chain directory to copy 392 if len(ctx.Args()) < 1 { 393 utils.Fatalf("Source chaindata directory path argument missing") 394 } 395 if len(ctx.Args()) < 2 { 396 utils.Fatalf("Source ancient chain directory path argument missing") 397 } 398 // Initialize a new chain for the running node to sync into 399 stack := makeFullNode(ctx) 400 defer stack.Close() 401 402 chain, chainDb := utils.MakeChain(ctx, stack) 403 syncMode := *utils.GlobalTextMarshaler(ctx, utils.SyncModeFlag.Name).(*downloader.SyncMode) 404 405 var syncBloom *trie.SyncBloom 406 if syncMode == downloader.FastSync { 407 syncBloom = trie.NewSyncBloom(uint64(ctx.GlobalInt(utils.CacheFlag.Name)/2), chainDb) 408 } 409 dl := downloader.New(0, chainDb, syncBloom, new(event.TypeMux), chain, nil, nil) 410 411 // Create a source peer to satisfy downloader requests from 412 db, err := rawdb.NewLevelDBDatabaseWithFreezer(ctx.Args().First(), ctx.GlobalInt(utils.CacheFlag.Name)/2, 256, ctx.Args().Get(1), "") 413 if err != nil { 414 return err 415 } 416 hc, err := core.NewHeaderChain(db, chain.Config(), chain.Engine(), func() bool { return false }) 417 if err != nil { 418 return err 419 } 420 peer := downloader.NewFakePeer("local", db, hc, dl) 421 if err = dl.RegisterPeer("local", 63, peer); err != nil { 422 return err 423 } 424 // Synchronise with the simulated peer 425 start := time.Now() 426 427 currentHeader := hc.CurrentHeader() 428 if err = dl.Synchronise("local", currentHeader.Hash(), hc.GetTd(currentHeader.Hash(), currentHeader.Number.Uint64()), syncMode); err != nil { 429 return err 430 } 431 for dl.Synchronising() { 432 time.Sleep(10 * time.Millisecond) 433 } 434 fmt.Printf("Database copy done in %v\n", time.Since(start)) 435 436 // Compact the entire database to remove any sync overhead 437 start = time.Now() 438 fmt.Println("Compacting entire database...") 439 if err = db.Compact(nil, nil); err != nil { 440 utils.Fatalf("Compaction failed: %v", err) 441 } 442 fmt.Printf("Compaction done in %v.\n\n", time.Since(start)) 443 return nil 444 } 445 446 func removeDB(ctx *cli.Context) error { 447 stack, config := makeConfigNode(ctx) 448 449 // Remove the full node state database 450 path := stack.ResolvePath("chaindata") 451 if common.FileExist(path) { 452 confirmAndRemoveDB(path, "full node state database") 453 } else { 454 log.Info("Full node state database missing", "path", path) 455 } 456 // Remove the full node ancient database 457 path = config.Eth.DatabaseFreezer 458 switch { 459 case path == "": 460 path = filepath.Join(stack.ResolvePath("chaindata"), "ancient") 461 case !filepath.IsAbs(path): 462 path = config.Node.ResolvePath(path) 463 } 464 if common.FileExist(path) { 465 confirmAndRemoveDB(path, "full node ancient database") 466 } else { 467 log.Info("Full node ancient database missing", "path", path) 468 } 469 // Remove the light node database 470 path = stack.ResolvePath("lightchaindata") 471 if common.FileExist(path) { 472 confirmAndRemoveDB(path, "light node database") 473 } else { 474 log.Info("Light node database missing", "path", path) 475 } 476 return nil 477 } 478 479 // confirmAndRemoveDB prompts the user for a last confirmation and removes the 480 // folder if accepted. 481 func confirmAndRemoveDB(database string, kind string) { 482 confirm, err := console.Stdin.PromptConfirm(fmt.Sprintf("Remove %s (%s)?", kind, database)) 483 switch { 484 case err != nil: 485 utils.Fatalf("%v", err) 486 case !confirm: 487 log.Info("Database deletion skipped", "path", database) 488 default: 489 start := time.Now() 490 filepath.Walk(database, func(path string, info os.FileInfo, err error) error { 491 // If we're at the top level folder, recurse into 492 if path == database { 493 return nil 494 } 495 // Delete all the files, but not subfolders 496 if !info.IsDir() { 497 os.Remove(path) 498 return nil 499 } 500 return filepath.SkipDir 501 }) 502 log.Info("Database successfully deleted", "path", database, "elapsed", common.PrettyDuration(time.Since(start))) 503 } 504 } 505 506 func dump(ctx *cli.Context) error { 507 stack := makeFullNode(ctx) 508 defer stack.Close() 509 510 chain, chainDb := utils.MakeChain(ctx, stack) 511 defer chainDb.Close() 512 for _, arg := range ctx.Args() { 513 var block *types.Block 514 if hashish(arg) { 515 block = chain.GetBlockByHash(common.HexToHash(arg)) 516 } else { 517 num, _ := strconv.Atoi(arg) 518 block = chain.GetBlockByNumber(uint64(num)) 519 } 520 if block == nil { 521 fmt.Println("{}") 522 utils.Fatalf("block not found") 523 } else { 524 state, err := state.New(block.Root(), state.NewDatabase(chainDb)) 525 if err != nil { 526 utils.Fatalf("could not create new state: %v", err) 527 } 528 excludeCode := ctx.Bool(utils.ExcludeCodeFlag.Name) 529 excludeStorage := ctx.Bool(utils.ExcludeStorageFlag.Name) 530 includeMissing := ctx.Bool(utils.IncludeIncompletesFlag.Name) 531 if ctx.Bool(utils.IterativeOutputFlag.Name) { 532 state.IterativeDump(excludeCode, excludeStorage, !includeMissing, json.NewEncoder(os.Stdout)) 533 } else { 534 if includeMissing { 535 fmt.Printf("If you want to include accounts with missing preimages, you need iterative output, since" + 536 " otherwise the accounts will overwrite each other in the resulting mapping.") 537 } 538 fmt.Printf("%v %s\n", includeMissing, state.Dump(excludeCode, excludeStorage, false)) 539 } 540 } 541 } 542 return nil 543 } 544 545 func inspect(ctx *cli.Context) error { 546 node, _ := makeConfigNode(ctx) 547 defer node.Close() 548 549 _, chainDb := utils.MakeChain(ctx, node) 550 defer chainDb.Close() 551 552 return rawdb.InspectDatabase(chainDb) 553 } 554 555 // hashish returns true for strings that look like hashes. 556 func hashish(x string) bool { 557 _, err := strconv.Atoi(x) 558 return err != nil 559 }