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