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