github.com/avence12/go-ethereum@v1.5.10-0.20170320123548-1dfd65f6d047/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 "fmt" 21 "os" 22 "runtime" 23 "strconv" 24 "sync/atomic" 25 "time" 26 27 "github.com/ethereum/go-ethereum/cmd/utils" 28 "github.com/ethereum/go-ethereum/common" 29 "github.com/ethereum/go-ethereum/console" 30 "github.com/ethereum/go-ethereum/core" 31 "github.com/ethereum/go-ethereum/core/state" 32 "github.com/ethereum/go-ethereum/core/types" 33 "github.com/ethereum/go-ethereum/ethdb" 34 "github.com/ethereum/go-ethereum/log" 35 "github.com/ethereum/go-ethereum/trie" 36 "github.com/syndtr/goleveldb/leveldb/util" 37 "gopkg.in/urfave/cli.v1" 38 ) 39 40 var ( 41 initCommand = cli.Command{ 42 Action: initGenesis, 43 Name: "init", 44 Usage: "Bootstrap and initialize a new genesis block", 45 ArgsUsage: "<genesisPath>", 46 Category: "BLOCKCHAIN COMMANDS", 47 Description: ` 48 The init command initializes a new genesis block and definition for the network. 49 This is a destructive action and changes the network in which you will be 50 participating. 51 `, 52 } 53 importCommand = cli.Command{ 54 Action: importChain, 55 Name: "import", 56 Usage: "Import a blockchain file", 57 ArgsUsage: "<filename> (<filename 2> ... <filename N>) ", 58 Category: "BLOCKCHAIN COMMANDS", 59 Description: ` 60 The import command imports blocks from an RLP-encoded form. The form can be one file 61 with several RLP-encoded blocks, or several files can be used. 62 If only one file is used, import error will result in failure. If several files are used, 63 processing will proceed even if an individual RLP-file import failure occurs. 64 `, 65 } 66 exportCommand = cli.Command{ 67 Action: exportChain, 68 Name: "export", 69 Usage: "Export blockchain into file", 70 ArgsUsage: "<filename> [<blockNumFirst> <blockNumLast>]", 71 Category: "BLOCKCHAIN COMMANDS", 72 Description: ` 73 Requires a first argument of the file to write to. 74 Optional second and third arguments control the first and 75 last block to write. In this mode, the file will be appended 76 if already existing. 77 `, 78 } 79 removedbCommand = cli.Command{ 80 Action: removeDB, 81 Name: "removedb", 82 Usage: "Remove blockchain and state databases", 83 ArgsUsage: " ", 84 Category: "BLOCKCHAIN COMMANDS", 85 Description: ` 86 TODO: Please write this 87 `, 88 } 89 dumpCommand = cli.Command{ 90 Action: dump, 91 Name: "dump", 92 Usage: "Dump a specific block from storage", 93 ArgsUsage: "[<blockHash> | <blockNum>]...", 94 Category: "BLOCKCHAIN COMMANDS", 95 Description: ` 96 The arguments are interpreted as block numbers or hashes. 97 Use "ethereum dump 0" to dump the genesis block. 98 `, 99 } 100 ) 101 102 // initGenesis will initialise the given JSON format genesis file and writes it as 103 // the zero'd block (i.e. genesis) or will fail hard if it can't succeed. 104 func initGenesis(ctx *cli.Context) error { 105 genesisPath := ctx.Args().First() 106 if len(genesisPath) == 0 { 107 utils.Fatalf("must supply path to genesis JSON file") 108 } 109 110 stack := makeFullNode(ctx) 111 chaindb := utils.MakeChainDatabase(ctx, stack) 112 113 genesisFile, err := os.Open(genesisPath) 114 if err != nil { 115 utils.Fatalf("failed to read genesis file: %v", err) 116 } 117 defer genesisFile.Close() 118 119 block, err := core.WriteGenesisBlock(chaindb, genesisFile) 120 if err != nil { 121 utils.Fatalf("failed to write genesis block: %v", err) 122 } 123 log.Info("Successfully wrote genesis state", "hash", block.Hash()) 124 return nil 125 } 126 127 func importChain(ctx *cli.Context) error { 128 if len(ctx.Args()) < 1 { 129 utils.Fatalf("This command requires an argument.") 130 } 131 stack := makeFullNode(ctx) 132 chain, chainDb := utils.MakeChain(ctx, stack) 133 defer chainDb.Close() 134 135 // Start periodically gathering memory profiles 136 var peakMemAlloc, peakMemSys uint64 137 go func() { 138 stats := new(runtime.MemStats) 139 for { 140 runtime.ReadMemStats(stats) 141 if atomic.LoadUint64(&peakMemAlloc) < stats.Alloc { 142 atomic.StoreUint64(&peakMemAlloc, stats.Alloc) 143 } 144 if atomic.LoadUint64(&peakMemSys) < stats.Sys { 145 atomic.StoreUint64(&peakMemSys, stats.Sys) 146 } 147 time.Sleep(5 * time.Second) 148 } 149 }() 150 // Import the chain 151 start := time.Now() 152 153 if len(ctx.Args()) == 1 { 154 if err := utils.ImportChain(chain, ctx.Args().First()); err != nil { 155 utils.Fatalf("Import error: %v", err) 156 } 157 } else { 158 for _, arg := range ctx.Args() { 159 if err := utils.ImportChain(chain, arg); err != nil { 160 log.Error("Import error", "file", arg, "err", err) 161 } 162 } 163 } 164 165 fmt.Printf("Import done in %v.\n\n", time.Since(start)) 166 167 // Output pre-compaction stats mostly to see the import trashing 168 db := chainDb.(*ethdb.LDBDatabase) 169 170 stats, err := db.LDB().GetProperty("leveldb.stats") 171 if err != nil { 172 utils.Fatalf("Failed to read database stats: %v", err) 173 } 174 fmt.Println(stats) 175 fmt.Printf("Trie cache misses: %d\n", trie.CacheMisses()) 176 fmt.Printf("Trie cache unloads: %d\n\n", trie.CacheUnloads()) 177 178 // Print the memory statistics used by the importing 179 mem := new(runtime.MemStats) 180 runtime.ReadMemStats(mem) 181 182 fmt.Printf("Object memory: %.3f MB current, %.3f MB peak\n", float64(mem.Alloc)/1024/1024, float64(atomic.LoadUint64(&peakMemAlloc))/1024/1024) 183 fmt.Printf("System memory: %.3f MB current, %.3f MB peak\n", float64(mem.Sys)/1024/1024, float64(atomic.LoadUint64(&peakMemSys))/1024/1024) 184 fmt.Printf("Allocations: %.3f million\n", float64(mem.Mallocs)/1000000) 185 fmt.Printf("GC pause: %v\n\n", time.Duration(mem.PauseTotalNs)) 186 187 if ctx.GlobalIsSet(utils.NoCompactionFlag.Name) { 188 return nil 189 } 190 191 // Compact the entire database to more accurately measure disk io and print the stats 192 start = time.Now() 193 fmt.Println("Compacting entire database...") 194 if err = db.LDB().CompactRange(util.Range{}); err != nil { 195 utils.Fatalf("Compaction failed: %v", err) 196 } 197 fmt.Printf("Compaction done in %v.\n\n", time.Since(start)) 198 199 stats, err = db.LDB().GetProperty("leveldb.stats") 200 if err != nil { 201 utils.Fatalf("Failed to read database stats: %v", err) 202 } 203 fmt.Println(stats) 204 205 return nil 206 } 207 208 func exportChain(ctx *cli.Context) error { 209 if len(ctx.Args()) < 1 { 210 utils.Fatalf("This command requires an argument.") 211 } 212 stack := makeFullNode(ctx) 213 chain, _ := utils.MakeChain(ctx, stack) 214 start := time.Now() 215 216 var err error 217 fp := ctx.Args().First() 218 if len(ctx.Args()) < 3 { 219 err = utils.ExportChain(chain, fp) 220 } else { 221 // This can be improved to allow for numbers larger than 9223372036854775807 222 first, ferr := strconv.ParseInt(ctx.Args().Get(1), 10, 64) 223 last, lerr := strconv.ParseInt(ctx.Args().Get(2), 10, 64) 224 if ferr != nil || lerr != nil { 225 utils.Fatalf("Export error in parsing parameters: block number not an integer\n") 226 } 227 if first < 0 || last < 0 { 228 utils.Fatalf("Export error: block number must be greater than 0\n") 229 } 230 err = utils.ExportAppendChain(chain, fp, uint64(first), uint64(last)) 231 } 232 233 if err != nil { 234 utils.Fatalf("Export error: %v\n", err) 235 } 236 fmt.Printf("Export done in %v", time.Since(start)) 237 return nil 238 } 239 240 func removeDB(ctx *cli.Context) error { 241 stack := utils.MakeNode(ctx, clientIdentifier, gitCommit) 242 dbdir := stack.ResolvePath(utils.ChainDbName(ctx)) 243 if !common.FileExist(dbdir) { 244 fmt.Println(dbdir, "does not exist") 245 return nil 246 } 247 248 fmt.Println(dbdir) 249 confirm, err := console.Stdin.PromptConfirm("Remove this database?") 250 switch { 251 case err != nil: 252 utils.Fatalf("%v", err) 253 case !confirm: 254 fmt.Println("Operation aborted") 255 default: 256 fmt.Println("Removing...") 257 start := time.Now() 258 os.RemoveAll(dbdir) 259 fmt.Printf("Removed in %v\n", time.Since(start)) 260 } 261 return nil 262 } 263 264 func dump(ctx *cli.Context) error { 265 stack := makeFullNode(ctx) 266 chain, chainDb := utils.MakeChain(ctx, stack) 267 for _, arg := range ctx.Args() { 268 var block *types.Block 269 if hashish(arg) { 270 block = chain.GetBlockByHash(common.HexToHash(arg)) 271 } else { 272 num, _ := strconv.Atoi(arg) 273 block = chain.GetBlockByNumber(uint64(num)) 274 } 275 if block == nil { 276 fmt.Println("{}") 277 utils.Fatalf("block not found") 278 } else { 279 state, err := state.New(block.Root(), chainDb) 280 if err != nil { 281 utils.Fatalf("could not create new state: %v", err) 282 } 283 fmt.Printf("%s\n", state.Dump()) 284 } 285 } 286 chainDb.Close() 287 return nil 288 } 289 290 // hashish returns true for strings that look like hashes. 291 func hashish(x string) bool { 292 _, err := strconv.Atoi(x) 293 return err != nil 294 }