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