github.com/shyftnetwork/go-empyrean@v1.8.3-0.20191127201940-fbfca9338f04/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/ShyftNetwork/go-empyrean/cmd/utils" 29 "github.com/ShyftNetwork/go-empyrean/common" 30 "github.com/ShyftNetwork/go-empyrean/console" 31 "github.com/ShyftNetwork/go-empyrean/core" 32 "github.com/ShyftNetwork/go-empyrean/core/state" 33 "github.com/ShyftNetwork/go-empyrean/core/types" 34 "github.com/ShyftNetwork/go-empyrean/eth/downloader" 35 "github.com/ShyftNetwork/go-empyrean/ethdb" 36 "github.com/ShyftNetwork/go-empyrean/event" 37 "github.com/ShyftNetwork/go-empyrean/log" 38 "github.com/ShyftNetwork/go-empyrean/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 }, 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 }, 166 Category: "BLOCKCHAIN COMMANDS", 167 Description: ` 168 The arguments are interpreted as block numbers or hashes. 169 Use "ethereum dump 0" to dump the genesis block.`, 170 } 171 ) 172 173 // initGenesis will initialise the given JSON format genesis file and writes it as 174 // the zero'd block (i.e. genesis) or will fail hard if it can't succeed. 175 func initGenesis(ctx *cli.Context) error { 176 // Make sure we have a valid genesis JSON 177 genesisPath := ctx.Args().First() 178 if len(genesisPath) == 0 { 179 utils.Fatalf("Must supply path to genesis JSON file") 180 } 181 file, err := os.Open(genesisPath) 182 if err != nil { 183 utils.Fatalf("Failed to read genesis file: %v", err) 184 } 185 defer file.Close() 186 187 genesis := new(core.Genesis) 188 if err := json.NewDecoder(file).Decode(genesis); err != nil { 189 utils.Fatalf("invalid genesis file: %v", err) 190 } 191 // Open an initialise both full and light databases 192 stack := makeFullNode(ctx) 193 for _, name := range []string{"chaindata", "lightchaindata"} { 194 var shyftdb ethdb.SDatabase 195 chaindb, err := stack.OpenDatabase(name, 0, 0) 196 if err != nil { 197 utils.Fatalf("Failed to open database: %v", err) 198 } 199 _, ok := os.LookupEnv("DISABLEPG") 200 if ok { 201 shyftdb = nil 202 } else if ctx.GlobalBool(utils.PostgresFlag.Name) { 203 shyftdb = nil 204 } else { 205 shyftdb, err = stack.OpenShyftDatabase() 206 if err != nil { 207 utils.Fatalf("Failed to open SHYFT database: %v", err) 208 } 209 } 210 _, hash, err := core.SetupGenesisBlock(chaindb, shyftdb, genesis) 211 if err != nil { 212 utils.Fatalf("Failed to write genesis block: %v", err) 213 } 214 log.Info("Successfully wrote genesis state", "database", name, "hash", hash) 215 } 216 return nil 217 } 218 219 func importChain(ctx *cli.Context) error { 220 if len(ctx.Args()) < 1 { 221 utils.Fatalf("This command requires an argument.") 222 } 223 stack := makeFullNode(ctx) 224 chain, chainDb, _ := utils.MakeChain(ctx, stack) 225 defer chainDb.Close() 226 227 // Start periodically gathering memory profiles 228 var peakMemAlloc, peakMemSys uint64 229 go func() { 230 stats := new(runtime.MemStats) 231 for { 232 runtime.ReadMemStats(stats) 233 if atomic.LoadUint64(&peakMemAlloc) < stats.Alloc { 234 atomic.StoreUint64(&peakMemAlloc, stats.Alloc) 235 } 236 if atomic.LoadUint64(&peakMemSys) < stats.Sys { 237 atomic.StoreUint64(&peakMemSys, stats.Sys) 238 } 239 time.Sleep(5 * time.Second) 240 } 241 }() 242 // Import the chain 243 start := time.Now() 244 245 if len(ctx.Args()) == 1 { 246 if err := utils.ImportChain(chain, ctx.Args().First()); err != nil { 247 log.Error("Import error", "err", err) 248 } 249 } else { 250 for _, arg := range ctx.Args() { 251 if err := utils.ImportChain(chain, arg); err != nil { 252 log.Error("Import error", "file", arg, "err", err) 253 } 254 } 255 } 256 chain.Stop() 257 fmt.Printf("Import done in %v.\n\n", time.Since(start)) 258 259 // Output pre-compaction stats mostly to see the import trashing 260 db := chainDb.(*ethdb.LDBDatabase) 261 262 stats, err := db.LDB().GetProperty("leveldb.stats") 263 if err != nil { 264 utils.Fatalf("Failed to read database stats: %v", err) 265 } 266 fmt.Println(stats) 267 268 ioStats, err := db.LDB().GetProperty("leveldb.iostats") 269 if err != nil { 270 utils.Fatalf("Failed to read database iostats: %v", err) 271 } 272 fmt.Println(ioStats) 273 274 fmt.Printf("Trie cache misses: %d\n", trie.CacheMisses()) 275 fmt.Printf("Trie cache unloads: %d\n\n", trie.CacheUnloads()) 276 277 // Print the memory statistics used by the importing 278 mem := new(runtime.MemStats) 279 runtime.ReadMemStats(mem) 280 281 fmt.Printf("Object memory: %.3f MB current, %.3f MB peak\n", float64(mem.Alloc)/1024/1024, float64(atomic.LoadUint64(&peakMemAlloc))/1024/1024) 282 fmt.Printf("System memory: %.3f MB current, %.3f MB peak\n", float64(mem.Sys)/1024/1024, float64(atomic.LoadUint64(&peakMemSys))/1024/1024) 283 fmt.Printf("Allocations: %.3f million\n", float64(mem.Mallocs)/1000000) 284 fmt.Printf("GC pause: %v\n\n", time.Duration(mem.PauseTotalNs)) 285 286 if ctx.GlobalIsSet(utils.NoCompactionFlag.Name) { 287 return nil 288 } 289 290 // Compact the entire database to more accurately measure disk io and print the stats 291 start = time.Now() 292 fmt.Println("Compacting entire database...") 293 if err = db.LDB().CompactRange(util.Range{}); err != nil { 294 utils.Fatalf("Compaction failed: %v", err) 295 } 296 fmt.Printf("Compaction done in %v.\n\n", time.Since(start)) 297 298 stats, err = db.LDB().GetProperty("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.LDB().GetProperty("leveldb.iostats") 305 if err != nil { 306 utils.Fatalf("Failed to read database iostats: %v", err) 307 } 308 fmt.Println(ioStats) 309 310 return nil 311 } 312 313 func exportChain(ctx *cli.Context) error { 314 if len(ctx.Args()) < 1 { 315 utils.Fatalf("This command requires an argument.") 316 } 317 stack := makeFullNode(ctx) 318 chain, _, _ := utils.MakeChain(ctx, stack) 319 start := time.Now() 320 321 var err error 322 fp := ctx.Args().First() 323 if len(ctx.Args()) < 3 { 324 err = utils.ExportChain(chain, fp) 325 } else { 326 // This can be improved to allow for numbers larger than 9223372036854775807 327 first, ferr := strconv.ParseInt(ctx.Args().Get(1), 10, 64) 328 last, lerr := strconv.ParseInt(ctx.Args().Get(2), 10, 64) 329 if ferr != nil || lerr != nil { 330 utils.Fatalf("Export error in parsing parameters: block number not an integer\n") 331 } 332 if first < 0 || last < 0 { 333 utils.Fatalf("Export error: block number must be greater than 0\n") 334 } 335 err = utils.ExportAppendChain(chain, fp, uint64(first), uint64(last)) 336 } 337 338 if err != nil { 339 utils.Fatalf("Export error: %v\n", err) 340 } 341 fmt.Printf("Export done in %v\n", time.Since(start)) 342 return nil 343 } 344 345 // importPreimages imports preimage data from the specified file. 346 func importPreimages(ctx *cli.Context) error { 347 if len(ctx.Args()) < 1 { 348 utils.Fatalf("This command requires an argument.") 349 } 350 stack := makeFullNode(ctx) 351 diskdb, _ := utils.MakeChainDatabase(ctx, stack).(*ethdb.LDBDatabase) 352 shyftdb := utils.MakeChainShyftDatabase(ctx, stack).(*ethdb.SPGDatabase) 353 354 start := time.Now() 355 if err := utils.ImportPreimages(diskdb, shyftdb, ctx.Args().First()); err != nil { 356 utils.Fatalf("Import error: %v\n", err) 357 } 358 fmt.Printf("Import done in %v\n", time.Since(start)) 359 return nil 360 } 361 362 // exportPreimages dumps the preimage data to specified json file in streaming way. 363 func exportPreimages(ctx *cli.Context) error { 364 if len(ctx.Args()) < 1 { 365 utils.Fatalf("This command requires an argument.") 366 } 367 stack := makeFullNode(ctx) 368 diskdb := utils.MakeChainDatabase(ctx, stack).(*ethdb.LDBDatabase) 369 shyftdb := utils.MakeChainShyftDatabase(ctx, stack).(*ethdb.SPGDatabase) 370 371 start := time.Now() 372 if err := utils.ExportPreimages(diskdb, shyftdb, ctx.Args().First()); 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 func copyDb(ctx *cli.Context) error { 380 // Ensure we have a source chain directory to copy 381 if len(ctx.Args()) != 1 { 382 utils.Fatalf("Source chaindata directory path argument missing") 383 } 384 // Initialize a new chain for the running node to sync into 385 stack := makeFullNode(ctx) 386 chain, chainDb, shyftDb := utils.MakeChain(ctx, stack) 387 388 syncmode := *utils.GlobalTextMarshaler(ctx, utils.SyncModeFlag.Name).(*downloader.SyncMode) 389 dl := downloader.New(syncmode, chainDb, shyftDb, new(event.TypeMux), chain, nil, nil) 390 391 // Create a source peer to satisfy downloader requests from 392 db, err := ethdb.NewLDBDatabase(ctx.Args().First(), ctx.GlobalInt(utils.CacheFlag.Name), 256) 393 if err != nil { 394 return err 395 } 396 hc, err := core.NewHeaderChain(db, chain.Config(), chain.Engine(), func() bool { return false }) 397 if err != nil { 398 return err 399 } 400 peer := downloader.NewFakePeer("local", db, hc, dl) 401 if err = dl.RegisterPeer("local", 63, peer); err != nil { 402 return err 403 } 404 // Synchronise with the simulated peer 405 start := time.Now() 406 407 currentHeader := hc.CurrentHeader() 408 if err = dl.Synchronise("local", currentHeader.Hash(), hc.GetTd(currentHeader.Hash(), currentHeader.Number.Uint64()), syncmode); err != nil { 409 return err 410 } 411 for dl.Synchronising() { 412 time.Sleep(10 * time.Millisecond) 413 } 414 fmt.Printf("Database copy done in %v\n", time.Since(start)) 415 416 // Compact the entire database to remove any sync overhead 417 start = time.Now() 418 fmt.Println("Compacting entire database...") 419 if err = chainDb.(*ethdb.LDBDatabase).LDB().CompactRange(util.Range{}); err != nil { 420 utils.Fatalf("Compaction failed: %v", err) 421 } 422 fmt.Printf("Compaction done in %v.\n\n", time.Since(start)) 423 424 return nil 425 } 426 427 func removeDB(ctx *cli.Context) error { 428 stack, _ := makeConfigNode(ctx) 429 430 for _, name := range []string{"chaindata", "lightchaindata"} { 431 // Ensure the database exists in the first place 432 logger := log.New("database", name) 433 434 dbdir := stack.ResolvePath(name) 435 if !common.FileExist(dbdir) { 436 logger.Info("Database doesn't exist, skipping", "path", dbdir) 437 continue 438 } 439 // Confirm removal and execute 440 fmt.Println(dbdir) 441 confirm, err := console.Stdin.PromptConfirm("Remove this database?") 442 switch { 443 case err != nil: 444 utils.Fatalf("%v", err) 445 case !confirm: 446 logger.Warn("Database deletion aborted") 447 default: 448 start := time.Now() 449 os.RemoveAll(dbdir) 450 logger.Info("Database successfully deleted", "elapsed", common.PrettyDuration(time.Since(start))) 451 } 452 } 453 return nil 454 } 455 456 func dump(ctx *cli.Context) error { 457 stack := makeFullNode(ctx) 458 chain, chainDb, _ := utils.MakeChain(ctx, stack) 459 for _, arg := range ctx.Args() { 460 var block *types.Block 461 if hashish(arg) { 462 block = chain.GetBlockByHash(common.HexToHash(arg)) 463 } else { 464 num, _ := strconv.Atoi(arg) 465 block = chain.GetBlockByNumber(uint64(num)) 466 } 467 if block == nil { 468 fmt.Println("{}") 469 utils.Fatalf("block not found") 470 } else { 471 state, err := state.New(block.Root(), state.NewDatabase(chainDb)) 472 if err != nil { 473 utils.Fatalf("could not create new state: %v", err) 474 } 475 fmt.Printf("%s\n", state.Dump()) 476 } 477 } 478 chainDb.Close() 479 return nil 480 } 481 482 // hashish returns true for strings that look like hashes. 483 func hashish(x string) bool { 484 _, err := strconv.Atoi(x) 485 return err != nil 486 }