github.com/daragao/go-ethereum@v1.8.14-0.20180809141559-45eaef243198/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 "runtime" 24 "strconv" 25 "sync/atomic" 26 "time" 27 28 "github.com/ethereum/go-ethereum/cmd/utils" 29 "github.com/ethereum/go-ethereum/common" 30 "github.com/ethereum/go-ethereum/console" 31 "github.com/ethereum/go-ethereum/core" 32 "github.com/ethereum/go-ethereum/core/state" 33 "github.com/ethereum/go-ethereum/core/types" 34 "github.com/ethereum/go-ethereum/eth/downloader" 35 "github.com/ethereum/go-ethereum/ethdb" 36 "github.com/ethereum/go-ethereum/event" 37 "github.com/ethereum/go-ethereum/log" 38 "github.com/ethereum/go-ethereum/trie" 39 "github.com/syndtr/goleveldb/leveldb/util" 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 utils.LightModeFlag, 52 }, 53 Category: "BLOCKCHAIN COMMANDS", 54 Description: ` 55 The init command initializes a new genesis block and definition for the network. 56 This is a destructive action and changes the network in which you will be 57 participating. 58 59 It expects the genesis file as argument.`, 60 } 61 importCommand = cli.Command{ 62 Action: utils.MigrateFlags(importChain), 63 Name: "import", 64 Usage: "Import a blockchain file", 65 ArgsUsage: "<filename> (<filename 2> ... <filename N>) ", 66 Flags: []cli.Flag{ 67 utils.DataDirFlag, 68 utils.CacheFlag, 69 utils.LightModeFlag, 70 utils.GCModeFlag, 71 utils.CacheDatabaseFlag, 72 utils.CacheGCFlag, 73 }, 74 Category: "BLOCKCHAIN COMMANDS", 75 Description: ` 76 The import command imports blocks from an RLP-encoded form. The form can be one file 77 with several RLP-encoded blocks, or several files can be used. 78 79 If only one file is used, import error will result in failure. If several files are used, 80 processing will proceed even if an individual RLP-file import failure occurs.`, 81 } 82 exportCommand = cli.Command{ 83 Action: utils.MigrateFlags(exportChain), 84 Name: "export", 85 Usage: "Export blockchain into file", 86 ArgsUsage: "<filename> [<blockNumFirst> <blockNumLast>]", 87 Flags: []cli.Flag{ 88 utils.DataDirFlag, 89 utils.CacheFlag, 90 utils.LightModeFlag, 91 }, 92 Category: "BLOCKCHAIN COMMANDS", 93 Description: ` 94 Requires a first argument of the file to write to. 95 Optional second and third arguments control the first and 96 last block to write. In this mode, the file will be appended 97 if already existing. If the file ends with .gz, the output will 98 be gzipped.`, 99 } 100 importPreimagesCommand = cli.Command{ 101 Action: utils.MigrateFlags(importPreimages), 102 Name: "import-preimages", 103 Usage: "Import the preimage database from an RLP stream", 104 ArgsUsage: "<datafile>", 105 Flags: []cli.Flag{ 106 utils.DataDirFlag, 107 utils.CacheFlag, 108 utils.LightModeFlag, 109 }, 110 Category: "BLOCKCHAIN COMMANDS", 111 Description: ` 112 The import-preimages command imports hash preimages from an RLP encoded stream.`, 113 } 114 exportPreimagesCommand = cli.Command{ 115 Action: utils.MigrateFlags(exportPreimages), 116 Name: "export-preimages", 117 Usage: "Export the preimage database into an RLP stream", 118 ArgsUsage: "<dumpfile>", 119 Flags: []cli.Flag{ 120 utils.DataDirFlag, 121 utils.CacheFlag, 122 utils.LightModeFlag, 123 }, 124 Category: "BLOCKCHAIN COMMANDS", 125 Description: ` 126 The export-preimages command export hash preimages to an RLP encoded stream`, 127 } 128 copydbCommand = cli.Command{ 129 Action: utils.MigrateFlags(copyDb), 130 Name: "copydb", 131 Usage: "Create a local chain from a target chaindata folder", 132 ArgsUsage: "<sourceChaindataDir>", 133 Flags: []cli.Flag{ 134 utils.DataDirFlag, 135 utils.CacheFlag, 136 utils.SyncModeFlag, 137 utils.FakePoWFlag, 138 utils.TestnetFlag, 139 utils.RinkebyFlag, 140 }, 141 Category: "BLOCKCHAIN COMMANDS", 142 Description: ` 143 The first argument must be the directory containing the blockchain to download from`, 144 } 145 removedbCommand = cli.Command{ 146 Action: utils.MigrateFlags(removeDB), 147 Name: "removedb", 148 Usage: "Remove blockchain and state databases", 149 ArgsUsage: " ", 150 Flags: []cli.Flag{ 151 utils.DataDirFlag, 152 utils.LightModeFlag, 153 }, 154 Category: "BLOCKCHAIN COMMANDS", 155 Description: ` 156 Remove blockchain and state databases`, 157 } 158 dumpCommand = cli.Command{ 159 Action: utils.MigrateFlags(dump), 160 Name: "dump", 161 Usage: "Dump a specific block from storage", 162 ArgsUsage: "[<blockHash> | <blockNum>]...", 163 Flags: []cli.Flag{ 164 utils.DataDirFlag, 165 utils.CacheFlag, 166 utils.LightModeFlag, 167 }, 168 Category: "BLOCKCHAIN COMMANDS", 169 Description: ` 170 The arguments are interpreted as block numbers or hashes. 171 Use "ethereum dump 0" to dump the genesis block.`, 172 } 173 ) 174 175 // initGenesis will initialise the given JSON format genesis file and writes it as 176 // the zero'd block (i.e. genesis) or will fail hard if it can't succeed. 177 func initGenesis(ctx *cli.Context) error { 178 // Make sure we have a valid genesis JSON 179 genesisPath := ctx.Args().First() 180 if len(genesisPath) == 0 { 181 utils.Fatalf("Must supply path to genesis JSON file") 182 } 183 file, err := os.Open(genesisPath) 184 if err != nil { 185 utils.Fatalf("Failed to read genesis file: %v", err) 186 } 187 defer file.Close() 188 189 genesis := new(core.Genesis) 190 if err := json.NewDecoder(file).Decode(genesis); err != nil { 191 utils.Fatalf("invalid genesis file: %v", err) 192 } 193 // Open an initialise both full and light databases 194 stack := makeFullNode(ctx) 195 for _, name := range []string{"chaindata", "lightchaindata"} { 196 chaindb, err := stack.OpenDatabase(name, 0, 0) 197 if err != nil { 198 utils.Fatalf("Failed to open database: %v", err) 199 } 200 _, hash, err := core.SetupGenesisBlock(chaindb, genesis) 201 if err != nil { 202 utils.Fatalf("Failed to write genesis block: %v", err) 203 } 204 log.Info("Successfully wrote genesis state", "database", name, "hash", hash) 205 } 206 return nil 207 } 208 209 func importChain(ctx *cli.Context) error { 210 if len(ctx.Args()) < 1 { 211 utils.Fatalf("This command requires an argument.") 212 } 213 stack := makeFullNode(ctx) 214 chain, chainDb := utils.MakeChain(ctx, stack) 215 defer chainDb.Close() 216 217 // Start periodically gathering memory profiles 218 var peakMemAlloc, peakMemSys uint64 219 go func() { 220 stats := new(runtime.MemStats) 221 for { 222 runtime.ReadMemStats(stats) 223 if atomic.LoadUint64(&peakMemAlloc) < stats.Alloc { 224 atomic.StoreUint64(&peakMemAlloc, stats.Alloc) 225 } 226 if atomic.LoadUint64(&peakMemSys) < stats.Sys { 227 atomic.StoreUint64(&peakMemSys, stats.Sys) 228 } 229 time.Sleep(5 * time.Second) 230 } 231 }() 232 // Import the chain 233 start := time.Now() 234 235 if len(ctx.Args()) == 1 { 236 if err := utils.ImportChain(chain, ctx.Args().First()); err != nil { 237 log.Error("Import error", "err", err) 238 } 239 } else { 240 for _, arg := range ctx.Args() { 241 if err := utils.ImportChain(chain, arg); err != nil { 242 log.Error("Import error", "file", arg, "err", err) 243 } 244 } 245 } 246 chain.Stop() 247 fmt.Printf("Import done in %v.\n\n", time.Since(start)) 248 249 // Output pre-compaction stats mostly to see the import trashing 250 db := chainDb.(*ethdb.LDBDatabase) 251 252 stats, err := db.LDB().GetProperty("leveldb.stats") 253 if err != nil { 254 utils.Fatalf("Failed to read database stats: %v", err) 255 } 256 fmt.Println(stats) 257 258 ioStats, err := db.LDB().GetProperty("leveldb.iostats") 259 if err != nil { 260 utils.Fatalf("Failed to read database iostats: %v", err) 261 } 262 fmt.Println(ioStats) 263 264 fmt.Printf("Trie cache misses: %d\n", trie.CacheMisses()) 265 fmt.Printf("Trie cache unloads: %d\n\n", trie.CacheUnloads()) 266 267 // Print the memory statistics used by the importing 268 mem := new(runtime.MemStats) 269 runtime.ReadMemStats(mem) 270 271 fmt.Printf("Object memory: %.3f MB current, %.3f MB peak\n", float64(mem.Alloc)/1024/1024, float64(atomic.LoadUint64(&peakMemAlloc))/1024/1024) 272 fmt.Printf("System memory: %.3f MB current, %.3f MB peak\n", float64(mem.Sys)/1024/1024, float64(atomic.LoadUint64(&peakMemSys))/1024/1024) 273 fmt.Printf("Allocations: %.3f million\n", float64(mem.Mallocs)/1000000) 274 fmt.Printf("GC pause: %v\n\n", time.Duration(mem.PauseTotalNs)) 275 276 if ctx.GlobalIsSet(utils.NoCompactionFlag.Name) { 277 return nil 278 } 279 280 // Compact the entire database to more accurately measure disk io and print the stats 281 start = time.Now() 282 fmt.Println("Compacting entire database...") 283 if err = db.LDB().CompactRange(util.Range{}); err != nil { 284 utils.Fatalf("Compaction failed: %v", err) 285 } 286 fmt.Printf("Compaction done in %v.\n\n", time.Since(start)) 287 288 stats, err = db.LDB().GetProperty("leveldb.stats") 289 if err != nil { 290 utils.Fatalf("Failed to read database stats: %v", err) 291 } 292 fmt.Println(stats) 293 294 ioStats, err = db.LDB().GetProperty("leveldb.iostats") 295 if err != nil { 296 utils.Fatalf("Failed to read database iostats: %v", err) 297 } 298 fmt.Println(ioStats) 299 300 return nil 301 } 302 303 func exportChain(ctx *cli.Context) error { 304 if len(ctx.Args()) < 1 { 305 utils.Fatalf("This command requires an argument.") 306 } 307 stack := makeFullNode(ctx) 308 chain, _ := utils.MakeChain(ctx, stack) 309 start := time.Now() 310 311 var err error 312 fp := ctx.Args().First() 313 if len(ctx.Args()) < 3 { 314 err = utils.ExportChain(chain, fp) 315 } else { 316 // This can be improved to allow for numbers larger than 9223372036854775807 317 first, ferr := strconv.ParseInt(ctx.Args().Get(1), 10, 64) 318 last, lerr := strconv.ParseInt(ctx.Args().Get(2), 10, 64) 319 if ferr != nil || lerr != nil { 320 utils.Fatalf("Export error in parsing parameters: block number not an integer\n") 321 } 322 if first < 0 || last < 0 { 323 utils.Fatalf("Export error: block number must be greater than 0\n") 324 } 325 err = utils.ExportAppendChain(chain, fp, uint64(first), uint64(last)) 326 } 327 328 if err != nil { 329 utils.Fatalf("Export error: %v\n", err) 330 } 331 fmt.Printf("Export done in %v\n", time.Since(start)) 332 return nil 333 } 334 335 // importPreimages imports preimage data from the specified file. 336 func importPreimages(ctx *cli.Context) error { 337 if len(ctx.Args()) < 1 { 338 utils.Fatalf("This command requires an argument.") 339 } 340 stack := makeFullNode(ctx) 341 diskdb := utils.MakeChainDatabase(ctx, stack).(*ethdb.LDBDatabase) 342 343 start := time.Now() 344 if err := utils.ImportPreimages(diskdb, ctx.Args().First()); err != nil { 345 utils.Fatalf("Export error: %v\n", err) 346 } 347 fmt.Printf("Export done in %v\n", time.Since(start)) 348 return nil 349 } 350 351 // exportPreimages dumps the preimage data to specified json file in streaming way. 352 func exportPreimages(ctx *cli.Context) error { 353 if len(ctx.Args()) < 1 { 354 utils.Fatalf("This command requires an argument.") 355 } 356 stack := makeFullNode(ctx) 357 diskdb := utils.MakeChainDatabase(ctx, stack).(*ethdb.LDBDatabase) 358 359 start := time.Now() 360 if err := utils.ExportPreimages(diskdb, ctx.Args().First()); err != nil { 361 utils.Fatalf("Export error: %v\n", err) 362 } 363 fmt.Printf("Export done in %v\n", time.Since(start)) 364 return nil 365 } 366 367 func copyDb(ctx *cli.Context) error { 368 // Ensure we have a source chain directory to copy 369 if len(ctx.Args()) != 1 { 370 utils.Fatalf("Source chaindata directory path argument missing") 371 } 372 // Initialize a new chain for the running node to sync into 373 stack := makeFullNode(ctx) 374 chain, chainDb := utils.MakeChain(ctx, stack) 375 376 syncmode := *utils.GlobalTextMarshaler(ctx, utils.SyncModeFlag.Name).(*downloader.SyncMode) 377 dl := downloader.New(syncmode, chainDb, new(event.TypeMux), chain, nil, nil) 378 379 // Create a source peer to satisfy downloader requests from 380 db, err := ethdb.NewLDBDatabase(ctx.Args().First(), ctx.GlobalInt(utils.CacheFlag.Name), 256) 381 if err != nil { 382 return err 383 } 384 hc, err := core.NewHeaderChain(db, chain.Config(), chain.Engine(), func() bool { return false }) 385 if err != nil { 386 return err 387 } 388 peer := downloader.NewFakePeer("local", db, hc, dl) 389 if err = dl.RegisterPeer("local", 63, peer); err != nil { 390 return err 391 } 392 // Synchronise with the simulated peer 393 start := time.Now() 394 395 currentHeader := hc.CurrentHeader() 396 if err = dl.Synchronise("local", currentHeader.Hash(), hc.GetTd(currentHeader.Hash(), currentHeader.Number.Uint64()), syncmode); err != nil { 397 return err 398 } 399 for dl.Synchronising() { 400 time.Sleep(10 * time.Millisecond) 401 } 402 fmt.Printf("Database copy done in %v\n", time.Since(start)) 403 404 // Compact the entire database to remove any sync overhead 405 start = time.Now() 406 fmt.Println("Compacting entire database...") 407 if err = chainDb.(*ethdb.LDBDatabase).LDB().CompactRange(util.Range{}); err != nil { 408 utils.Fatalf("Compaction failed: %v", err) 409 } 410 fmt.Printf("Compaction done in %v.\n\n", time.Since(start)) 411 412 return nil 413 } 414 415 func removeDB(ctx *cli.Context) error { 416 stack, _ := makeConfigNode(ctx) 417 418 for _, name := range []string{"chaindata", "lightchaindata"} { 419 // Ensure the database exists in the first place 420 logger := log.New("database", name) 421 422 dbdir := stack.ResolvePath(name) 423 if !common.FileExist(dbdir) { 424 logger.Info("Database doesn't exist, skipping", "path", dbdir) 425 continue 426 } 427 // Confirm removal and execute 428 fmt.Println(dbdir) 429 confirm, err := console.Stdin.PromptConfirm("Remove this database?") 430 switch { 431 case err != nil: 432 utils.Fatalf("%v", err) 433 case !confirm: 434 logger.Warn("Database deletion aborted") 435 default: 436 start := time.Now() 437 os.RemoveAll(dbdir) 438 logger.Info("Database successfully deleted", "elapsed", common.PrettyDuration(time.Since(start))) 439 } 440 } 441 return nil 442 } 443 444 func dump(ctx *cli.Context) error { 445 stack := makeFullNode(ctx) 446 chain, chainDb := utils.MakeChain(ctx, stack) 447 for _, arg := range ctx.Args() { 448 var block *types.Block 449 if hashish(arg) { 450 block = chain.GetBlockByHash(common.HexToHash(arg)) 451 } else { 452 num, _ := strconv.Atoi(arg) 453 block = chain.GetBlockByNumber(uint64(num)) 454 } 455 if block == nil { 456 fmt.Println("{}") 457 utils.Fatalf("block not found") 458 } else { 459 state, err := state.New(block.Root(), state.NewDatabase(chainDb)) 460 if err != nil { 461 utils.Fatalf("could not create new state: %v", err) 462 } 463 fmt.Printf("%s\n", state.Dump()) 464 } 465 } 466 chainDb.Close() 467 return nil 468 } 469 470 // hashish returns true for strings that look like hashes. 471 func hashish(x string) bool { 472 _, err := strconv.Atoi(x) 473 return err != nil 474 }