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